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