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@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 */
rgbe_error(int rgbe_error_code,const char * msg)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
float2rgbe(unsigned char rgbe[4],float red,float green,float blue)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
rgbe2float(float * red,float * green,float * blue,unsigned char rgbe[4])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 */
RGBE_WriteHeader(FILE * fp,int width,int height,rgbe_header_info * info)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 */
RGBE_ReadHeader(FILE * fp,int * width,int * height,rgbe_header_info * info)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 */
RGBE_WritePixels(FILE * fp,float * data,int numpixels)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 */
RGBE_ReadPixels(FILE * fp,float * data,int numpixels)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 
RGBE_WriteBytes_RLE(FILE * fp,unsigned char * data,int numbytes)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 
RGBE_WritePixels_RLE(FILE * fp,float * data,int scanline_width,int num_scanlines)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 
RGBE_ReadPixels_RLE(FILE * fp,float * data,int scanline_width,int num_scanlines)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