Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                           License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
     14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
     15 // Third party copyrights are property of their respective owners.
     16 //
     17 // Redistribution and use in source and binary forms, with or without modification,
     18 // are permitted provided that the following conditions are met:
     19 //
     20 //   * Redistribution's of source code must retain the above copyright notice,
     21 //     this list of conditions and the following disclaimer.
     22 //
     23 //   * Redistribution's in binary form must reproduce the above copyright notice,
     24 //     this list of conditions and the following disclaimer in the documentation
     25 //     and/or other materials provided with the distribution.
     26 //
     27 //   * The name of the copyright holders may not be used to endorse or promote products
     28 //     derived from this software without specific prior written permission.
     29 //
     30 // This software is provided by the copyright holders and contributors "as is" and
     31 // any express or implied warranties, including, but not limited to, the implied
     32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     33 // In no event shall the Intel Corporation or contributors be liable for any direct,
     34 // indirect, incidental, special, exemplary, or consequential damages
     35 // (including, but not limited to, procurement of substitute goods or services;
     36 // loss of use, data, or profits; or business interruption) however caused
     37 // and on any theory of liability, whether in contract, strict liability,
     38 // or tort (including negligence or otherwise) arising in any way out of
     39 // the use of this software, even if advised of the possibility of such damage.
     40 //
     41 //M*/
     42 
     43 #include "precomp.hpp"
     44 #include "rgbe.hpp"
     45 #include <math.h>
     46 #if !defined(__APPLE__)
     47 #include <malloc.h>
     48 #endif
     49 #include <string.h>
     50 #include <ctype.h>
     51 
     52 // This file contains code to read and write four byte rgbe file format
     53 // developed by Greg Ward.  It handles the conversions between rgbe and
     54 // pixels consisting of floats.  The data is assumed to be an array of floats.
     55 // By default there are three floats per pixel in the order red, green, blue.
     56 // (RGBE_DATA_??? values control this.)  Only the mimimal header reading and
     57 // writing is implemented.  Each routine does error checking and will return
     58 // a status value as defined below.  This code is intended as a skeleton so
     59 // feel free to modify it to suit your needs.
     60 
     61 // Some opencv specific changes have been added:
     62 // inline define specified, error handler uses CV_Error,
     63 // defines changed to work in bgr color space.
     64 //
     65 // posted to http://www.graphics.cornell.edu/~bjw/
     66 // written by Bruce Walter  (bjw (at) graphics.cornell.edu)  5/26/95
     67 // based on code written by Greg Ward
     68 
     69 #define INLINE inline
     70 
     71 /* offsets to red, green, and blue components in a data (float) pixel */
     72 #define RGBE_DATA_RED    2
     73 #define RGBE_DATA_GREEN  1
     74 #define RGBE_DATA_BLUE   0
     75 /* number of floats per pixel */
     76 #define RGBE_DATA_SIZE   3
     77 
     78 enum rgbe_error_codes {
     79   rgbe_read_error,
     80   rgbe_write_error,
     81   rgbe_format_error,
     82   rgbe_memory_error
     83 };
     84 
     85 /* default error routine.  change this to change error handling */
     86 static int rgbe_error(int rgbe_error_code, const char *msg)
     87 {
     88   switch (rgbe_error_code) {
     89   case rgbe_read_error:
     90        CV_Error(cv::Error::StsError, "RGBE read error");
     91        break;
     92   case rgbe_write_error:
     93        CV_Error(cv::Error::StsError, "RGBE write error");
     94        break;
     95   case rgbe_format_error:
     96        CV_Error(cv::Error::StsError, cv::String("RGBE bad file format: ") +
     97                        cv::String(msg));
     98        break;
     99   default:
    100   case rgbe_memory_error:
    101        CV_Error(cv::Error::StsError, cv::String("RGBE error: \n") +
    102                      cv::String(msg));
    103   }
    104   return RGBE_RETURN_FAILURE;
    105 }
    106 
    107 /* standard conversion from float pixels to rgbe pixels */
    108 /* note: you can remove the "inline"s if your compiler complains about it */
    109 static INLINE void
    110 float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
    111 {
    112   float v;
    113   int e;
    114 
    115   v = red;
    116   if (green > v) v = green;
    117   if (blue > v) v = blue;
    118   if (v < 1e-32) {
    119     rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
    120   }
    121   else {
    122     v = static_cast<float>(frexp(v,&e) * 256.0/v);
    123     rgbe[0] = (unsigned char) (red * v);
    124     rgbe[1] = (unsigned char) (green * v);
    125     rgbe[2] = (unsigned char) (blue * v);
    126     rgbe[3] = (unsigned char) (e + 128);
    127   }
    128 }
    129 
    130 /* standard conversion from rgbe to float pixels */
    131 /* note: Ward uses ldexp(col+0.5,exp-(128+8)).  However we wanted pixels */
    132 /*       in the range [0,1] to map back into the range [0,1].            */
    133 static INLINE void
    134 rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
    135 {
    136   float f;
    137 
    138   if (rgbe[3]) {   /*nonzero pixel*/
    139     f = static_cast<float>(ldexp(1.0,rgbe[3]-(int)(128+8)));
    140     *red = rgbe[0] * f;
    141     *green = rgbe[1] * f;
    142     *blue = rgbe[2] * f;
    143   }
    144   else
    145     *red = *green = *blue = 0.0;
    146 }
    147 
    148 /* default minimal header. modify if you want more information in header */
    149 int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info)
    150 {
    151   const char *programtype = "RGBE";
    152 
    153   if (info && (info->valid & RGBE_VALID_PROGRAMTYPE))
    154     programtype = info->programtype;
    155   if (fprintf(fp,"#?%s\n",programtype) < 0)
    156     return rgbe_error(rgbe_write_error,NULL);
    157   /* The #? is to identify file type, the programtype is optional. */
    158   if (info && (info->valid & RGBE_VALID_GAMMA)) {
    159     if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0)
    160       return rgbe_error(rgbe_write_error,NULL);
    161   }
    162   if (info && (info->valid & RGBE_VALID_EXPOSURE)) {
    163     if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0)
    164       return rgbe_error(rgbe_write_error,NULL);
    165   }
    166   if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0)
    167     return rgbe_error(rgbe_write_error,NULL);
    168   if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
    169     return rgbe_error(rgbe_write_error,NULL);
    170   return RGBE_RETURN_SUCCESS;
    171 }
    172 
    173 /* minimal header reading.  modify if you want to parse more information */
    174 int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info)
    175 {
    176   char buf[128];
    177   float tempf;
    178   int i;
    179 
    180   if (info) {
    181     info->valid = 0;
    182     info->programtype[0] = 0;
    183     info->gamma = info->exposure = 1.0;
    184   }
    185   if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL)
    186     return rgbe_error(rgbe_read_error,NULL);
    187   if ((buf[0] != '#')||(buf[1] != '?')) {
    188     /* if you want to require the magic token then uncomment the next line */
    189     /*return rgbe_error(rgbe_format_error,"bad initial token"); */
    190   }
    191   else if (info) {
    192     info->valid |= RGBE_VALID_PROGRAMTYPE;
    193     for(i=0;i<static_cast<int>(sizeof(info->programtype)-1);i++) {
    194       if ((buf[i+2] == 0) || isspace(buf[i+2]))
    195   break;
    196       info->programtype[i] = buf[i+2];
    197     }
    198     info->programtype[i] = 0;
    199     if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
    200       return rgbe_error(rgbe_read_error,NULL);
    201   }
    202   for(;;) {
    203     if ((buf[0] == 0)||(buf[0] == '\n'))
    204       return rgbe_error(rgbe_format_error,"no FORMAT specifier found");
    205     else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0)
    206       break;       /* format found so break out of loop */
    207     else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) {
    208       info->gamma = tempf;
    209       info->valid |= RGBE_VALID_GAMMA;
    210     }
    211     else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) {
    212       info->exposure = tempf;
    213       info->valid |= RGBE_VALID_EXPOSURE;
    214     }
    215     if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
    216       return rgbe_error(rgbe_read_error,NULL);
    217   }
    218   if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
    219     return rgbe_error(rgbe_read_error,NULL);
    220   if (strcmp(buf,"\n") != 0)
    221     return rgbe_error(rgbe_format_error,
    222           "missing blank line after FORMAT specifier");
    223   if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
    224     return rgbe_error(rgbe_read_error,NULL);
    225   if (sscanf(buf,"-Y %d +X %d",height,width) < 2)
    226     return rgbe_error(rgbe_format_error,"missing image size specifier");
    227   return RGBE_RETURN_SUCCESS;
    228 }
    229 
    230 /* simple write routine that does not use run length encoding */
    231 /* These routines can be made faster by allocating a larger buffer and
    232    fread-ing and fwrite-ing the data in larger chunks */
    233 int RGBE_WritePixels(FILE *fp, float *data, int numpixels)
    234 {
    235   unsigned char rgbe[4];
    236 
    237   while (numpixels-- > 0) {
    238     float2rgbe(rgbe,data[RGBE_DATA_RED],
    239          data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
    240     data += RGBE_DATA_SIZE;
    241     if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
    242       return rgbe_error(rgbe_write_error,NULL);
    243   }
    244   return RGBE_RETURN_SUCCESS;
    245 }
    246 
    247 /* simple read routine.  will not correctly handle run length encoding */
    248 int RGBE_ReadPixels(FILE *fp, float *data, int numpixels)
    249 {
    250   unsigned char rgbe[4];
    251 
    252   while(numpixels-- > 0) {
    253     if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
    254       return rgbe_error(rgbe_read_error,NULL);
    255     rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
    256          &data[RGBE_DATA_BLUE],rgbe);
    257     data += RGBE_DATA_SIZE;
    258   }
    259   return RGBE_RETURN_SUCCESS;
    260 }
    261 
    262 /* The code below is only needed for the run-length encoded files. */
    263 /* Run length encoding adds considerable complexity but does */
    264 /* save some space.  For each scanline, each channel (r,g,b,e) is */
    265 /* encoded separately for better compression. */
    266 
    267 static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes)
    268 {
    269 #define MINRUNLENGTH 4
    270   int cur, beg_run, run_count, old_run_count, nonrun_count;
    271   unsigned char buf[2];
    272 
    273   cur = 0;
    274   while(cur < numbytes) {
    275     beg_run = cur;
    276     /* find next run of length at least 4 if one exists */
    277     run_count = old_run_count = 0;
    278     while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
    279       beg_run += run_count;
    280       old_run_count = run_count;
    281       run_count = 1;
    282       while( (beg_run + run_count < numbytes) && (run_count < 127)
    283              && (data[beg_run] == data[beg_run + run_count]))
    284   run_count++;
    285     }
    286     /* if data before next big run is a short run then write it as such */
    287     if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
    288       buf[0] = static_cast<unsigned char>(128 + old_run_count);   /*write short run*/
    289       buf[1] = data[cur];
    290       if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
    291   return rgbe_error(rgbe_write_error,NULL);
    292       cur = beg_run;
    293     }
    294     /* write out bytes until we reach the start of the next run */
    295     while(cur < beg_run) {
    296       nonrun_count = beg_run - cur;
    297       if (nonrun_count > 128)
    298   nonrun_count = 128;
    299       buf[0] = static_cast<unsigned char>(nonrun_count);
    300       if (fwrite(buf,sizeof(buf[0]),1,fp) < 1)
    301   return rgbe_error(rgbe_write_error,NULL);
    302       if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1)
    303   return rgbe_error(rgbe_write_error,NULL);
    304       cur += nonrun_count;
    305     }
    306     /* write out next run if one was found */
    307     if (run_count >= MINRUNLENGTH) {
    308       buf[0] = static_cast<unsigned char>(128 + run_count);
    309       buf[1] = data[beg_run];
    310       if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
    311   return rgbe_error(rgbe_write_error,NULL);
    312       cur += run_count;
    313     }
    314   }
    315   return RGBE_RETURN_SUCCESS;
    316 #undef MINRUNLENGTH
    317 }
    318 
    319 int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
    320        int num_scanlines)
    321 {
    322   unsigned char rgbe[4];
    323   unsigned char *buffer;
    324   int i, err;
    325 
    326   if ((scanline_width < 8)||(scanline_width > 0x7fff))
    327     /* run length encoding is not allowed so write flat*/
    328     return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
    329   buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width);
    330   if (buffer == NULL)
    331     /* no buffer space so write flat */
    332     return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
    333   while(num_scanlines-- > 0) {
    334     rgbe[0] = 2;
    335     rgbe[1] = 2;
    336     rgbe[2] = static_cast<unsigned char>(scanline_width >> 8);
    337     rgbe[3] = scanline_width & 0xFF;
    338     if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
    339       free(buffer);
    340       return rgbe_error(rgbe_write_error,NULL);
    341     }
    342     for(i=0;i<scanline_width;i++) {
    343       float2rgbe(rgbe,data[RGBE_DATA_RED],
    344      data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
    345       buffer[i] = rgbe[0];
    346       buffer[i+scanline_width] = rgbe[1];
    347       buffer[i+2*scanline_width] = rgbe[2];
    348       buffer[i+3*scanline_width] = rgbe[3];
    349       data += RGBE_DATA_SIZE;
    350     }
    351     /* write out each of the four channels separately run length encoded */
    352     /* first red, then green, then blue, then exponent */
    353     for(i=0;i<4;i++) {
    354       if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width],
    355              scanline_width)) != RGBE_RETURN_SUCCESS) {
    356   free(buffer);
    357   return err;
    358       }
    359     }
    360   }
    361   free(buffer);
    362   return RGBE_RETURN_SUCCESS;
    363 }
    364 
    365 int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
    366       int num_scanlines)
    367 {
    368   unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
    369   int i, count;
    370   unsigned char buf[2];
    371 
    372   if ((scanline_width < 8)||(scanline_width > 0x7fff))
    373     /* run length encoding is not allowed so read flat*/
    374     return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
    375   scanline_buffer = NULL;
    376   /* read in each successive scanline */
    377   while(num_scanlines > 0) {
    378     if (fread(rgbe,sizeof(rgbe),1,fp) < 1) {
    379       free(scanline_buffer);
    380       return rgbe_error(rgbe_read_error,NULL);
    381     }
    382     if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
    383       /* this file is not run length encoded */
    384       rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],&data[RGBE_DATA_BLUE],rgbe);
    385       data += RGBE_DATA_SIZE;
    386       free(scanline_buffer);
    387       return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
    388     }
    389     if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
    390       free(scanline_buffer);
    391       return rgbe_error(rgbe_format_error,"wrong scanline width");
    392     }
    393     if (scanline_buffer == NULL)
    394       scanline_buffer = (unsigned char *)
    395   malloc(sizeof(unsigned char)*4*scanline_width);
    396     if (scanline_buffer == NULL)
    397       return rgbe_error(rgbe_memory_error,"unable to allocate buffer space");
    398 
    399     ptr = &scanline_buffer[0];
    400     /* read each of the four channels for the scanline into the buffer */
    401     for(i=0;i<4;i++) {
    402       ptr_end = &scanline_buffer[(i+1)*scanline_width];
    403       while(ptr < ptr_end) {
    404   if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) {
    405     free(scanline_buffer);
    406     return rgbe_error(rgbe_read_error,NULL);
    407   }
    408   if (buf[0] > 128) {
    409     /* a run of the same value */
    410     count = buf[0]-128;
    411     if ((count == 0)||(count > ptr_end - ptr)) {
    412       free(scanline_buffer);
    413       return rgbe_error(rgbe_format_error,"bad scanline data");
    414     }
    415     while(count-- > 0)
    416       *ptr++ = buf[1];
    417   }
    418   else {
    419     /* a non-run */
    420     count = buf[0];
    421     if ((count == 0)||(count > ptr_end - ptr)) {
    422       free(scanline_buffer);
    423       return rgbe_error(rgbe_format_error,"bad scanline data");
    424     }
    425     *ptr++ = buf[1];
    426     if (--count > 0) {
    427       if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) {
    428         free(scanline_buffer);
    429         return rgbe_error(rgbe_read_error,NULL);
    430       }
    431       ptr += count;
    432     }
    433   }
    434       }
    435     }
    436     /* now convert data from buffer into floats */
    437     for(i=0;i<scanline_width;i++) {
    438       rgbe[0] = scanline_buffer[i];
    439       rgbe[1] = scanline_buffer[i+scanline_width];
    440       rgbe[2] = scanline_buffer[i+2*scanline_width];
    441       rgbe[3] = scanline_buffer[i+3*scanline_width];
    442       rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
    443      &data[RGBE_DATA_BLUE],rgbe);
    444       data += RGBE_DATA_SIZE;
    445     }
    446     num_scanlines--;
    447   }
    448   free(scanline_buffer);
    449   return RGBE_RETURN_SUCCESS;
    450 }
    451