initial import
[dosrtxon] / libs / imago / src / filergbe.c
1 /* This file contains code to read and write four byte rgbe file format
2  * developed by Greg Ward.  It handles the conversions between rgbe and
3  * pixels consisting of floats.  The data is assumed to be an array of floats.
4  * By default there are three floats per pixel in the order red, green, blue.
5  * (RGBE_DATA_??? values control this.)
6  *
7  * written by Bruce Walter  (bjw@graphics.cornell.edu)  5/26/95
8  * based on code written by Greg Ward
9  * minor modifications by John Tsiombikas (nuclear@member.fsf.org) apr.9 2007
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <math.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include "imago2.h"
19 #include "ftmodule.h"
20
21
22 typedef struct {
23         int valid;                              /* indicate which fields are valid */
24         char programtype[16];   /* listed at beginning of file to identify it
25                                                          * after "#?".  defaults to "RGBE" */
26         float gamma;                    /* image has already been gamma corrected with
27                                                          * given gamma.  defaults to 1.0 (no correction) */
28         float exposure;                 /* a value of 1.0 in an image corresponds to
29                                                          * <exposure> watts/steradian/m^2.
30                                                          * defaults to 1.0 */
31 } rgbe_header_info;
32
33
34 static int check(struct img_io *io);
35 static int read(struct img_pixmap *img, struct img_io *io);
36 static int write(struct img_pixmap *img, struct img_io *io);
37
38 static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info);
39 static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines);
40
41
42 int img_register_rgbe(void)
43 {
44         static struct ftype_module mod = {".rgbe", check, read, write};
45         return img_register_module(&mod);
46 }
47
48
49 static int check(struct img_io *io)
50 {
51         int xsz, ysz, res;
52         long pos = io->seek(0, SEEK_CUR, io->uptr);
53
54         rgbe_header_info hdr;
55         res = rgbe_read_header(io, &xsz, &ysz, &hdr);
56
57         io->seek(pos, SEEK_SET, io->uptr);
58         return res;
59 }
60
61 static int read(struct img_pixmap *img, struct img_io *io)
62 {
63         int xsz, ysz;
64         rgbe_header_info hdr;
65
66         if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) {
67                 return -1;
68         }
69
70         if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) {
71                 return -1;
72         }
73         if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) {
74                 return -1;
75         }
76         return 0;
77 }
78
79 static int write(struct img_pixmap *img, struct img_io *io)
80 {
81         return -1;      /* TODO */
82 }
83
84
85 static int iofgetc(struct img_io *io)
86 {
87         char c;
88         return io->read(&c, 1, io->uptr) < 1 ? -1 : c;
89 }
90
91 static char *iofgets(char *buf, int size, struct img_io *io)
92 {
93         int c;
94         char *ptr = buf;
95
96         while(--size > 0 && (c = iofgetc(io)) != -1) {
97                 *ptr++ = c;
98                 if(c == '\n') break;
99         }
100         *ptr = 0;
101
102         return ptr == buf ? 0 : buf;
103 }
104
105
106 /* flags indicating which fields in an rgbe_header_info are valid */
107 #define RGBE_VALID_PROGRAMTYPE 0x01
108 #define RGBE_VALID_GAMMA       0x02
109 #define RGBE_VALID_EXPOSURE    0x04
110
111 /* return codes for rgbe routines */
112 #define RGBE_RETURN_SUCCESS 0
113 #define RGBE_RETURN_FAILURE -1
114
115
116 #if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L
117 #define INLINE inline
118 #else
119 #define INLINE
120 #endif
121
122 /* offsets to red, green, and blue components in a data (float) pixel */
123 #define RGBE_DATA_RED   0
124 #define RGBE_DATA_GREEN  1
125 #define RGBE_DATA_BLUE   2
126
127 /* number of floats per pixel */
128 #define RGBE_DATA_SIZE   3
129
130 enum rgbe_error_codes {
131         rgbe_read_error,
132         rgbe_write_error,
133         rgbe_format_error,
134         rgbe_memory_error
135 };
136
137
138 /* default error routine.  change this to change error handling */
139 static int rgbe_error(int rgbe_error_code, char *msg)
140 {
141         switch (rgbe_error_code) {
142         case rgbe_read_error:
143                 fprintf(stderr, "RGBE read error: %s\n", strerror(errno));
144                 break;
145
146         case rgbe_write_error:
147                 fprintf(stderr, "RGBE write error: %s\n", strerror(errno));
148                 break;
149
150         case rgbe_format_error:
151                 fprintf(stderr, "RGBE bad file format: %s\n", msg);
152                 break;
153
154         default:
155         case rgbe_memory_error:
156                 fprintf(stderr, "RGBE error: %s\n", msg);
157         }
158         return RGBE_RETURN_FAILURE;
159 }
160
161 /* standard conversion from float pixels to rgbe pixels */
162 /*static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
163 {
164         float v;
165         int e;
166
167         v = red;
168         if(green > v)
169                 v = green;
170         if(blue > v)
171                 v = blue;
172         if(v < 1e-32) {
173                 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
174         } else {
175                 v = frexp(v, &e) * 256.0 / v;
176                 rgbe[0] = (unsigned char)(red * v);
177                 rgbe[1] = (unsigned char)(green * v);
178                 rgbe[2] = (unsigned char)(blue * v);
179                 rgbe[3] = (unsigned char)(e + 128);
180         }
181 }*/
182
183 /* standard conversion from rgbe to float pixels */
184 /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
185 /*       in the range [0,1] to map back into the range [0,1]. */
186 static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
187 {
188         float f;
189
190         if(rgbe[3]) {                           /*nonzero pixel */
191                 f = ldexp(1.0, rgbe[3] - (int)(128 + 8));
192                 *red = rgbe[0] * f;
193                 *green = rgbe[1] * f;
194                 *blue = rgbe[2] * f;
195         } else
196                 *red = *green = *blue = 0.0;
197 }
198
199 #if 0
200 /* default minimal header. modify if you want more information in header */
201 static int rgbe_write_header(FILE * fp, int width, int height, rgbe_header_info * info)
202 {
203         char *programtype = "RGBE";
204
205         if(info && (info->valid & RGBE_VALID_PROGRAMTYPE))
206                 programtype = info->programtype;
207         if(fprintf(fp, "#?%s\n", programtype) < 0)
208                 return rgbe_error(rgbe_write_error, NULL);
209         /* The #? is to identify file type, the programtype is optional. */
210         if(info && (info->valid & RGBE_VALID_GAMMA)) {
211                 if(fprintf(fp, "GAMMA=%g\n", info->gamma) < 0)
212                         return rgbe_error(rgbe_write_error, NULL);
213         }
214         if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
215                 if(fprintf(fp, "EXPOSURE=%g\n", info->exposure) < 0)
216                         return rgbe_error(rgbe_write_error, NULL);
217         }
218         if(fprintf(fp, "FORMAT=32-bit_rle_rgbe\n\n") < 0)
219                 return rgbe_error(rgbe_write_error, NULL);
220         if(fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
221                 return rgbe_error(rgbe_write_error, NULL);
222         return RGBE_RETURN_SUCCESS;
223 }
224 #endif
225
226 /* minimal header reading.  modify if you want to parse more information */
227 static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info)
228 {
229         char buf[128];
230         float tempf;
231         int i;
232
233         if(info) {
234                 info->valid = 0;
235                 info->programtype[0] = 0;
236                 info->gamma = info->exposure = 1.0;
237         }
238         if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL)
239                 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
240         if((buf[0] != '#') || (buf[1] != '?')) {
241                 /* if you want to require the magic token then uncomment the next line */
242                 /*return rgbe_error(rgbe_format_error,"bad initial token"); */
243         } else if(info) {
244                 info->valid |= RGBE_VALID_PROGRAMTYPE;
245                 for(i = 0; i < sizeof(info->programtype) - 1; i++) {
246                         if((buf[i + 2] == 0) || isspace(buf[i + 2]))
247                                 break;
248                         info->programtype[i] = buf[i + 2];
249                 }
250                 info->programtype[i] = 0;
251                 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
252                         return rgbe_error(rgbe_read_error, NULL);
253         }
254         for(;;) {
255                 if((buf[0] == 0) || (buf[0] == '\n'))
256                         return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "no FORMAT specifier found");*/
257                 else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0)
258                         break;                          /* format found so break out of loop */
259                 else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) {
260                         info->gamma = tempf;
261                         info->valid |= RGBE_VALID_GAMMA;
262                 } else if(info && (sscanf(buf, "EXPOSURE=%g", &tempf) == 1)) {
263                         info->exposure = tempf;
264                         info->valid |= RGBE_VALID_EXPOSURE;
265                 }
266                 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
267                         return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
268         }
269         if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
270                 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
271         if(strcmp(buf, "\n") != 0)
272                 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");*/
273         if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
274                 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
275         if(sscanf(buf, "-Y %d +X %d", height, width) < 2)
276                 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing image size specifier");*/
277         return RGBE_RETURN_SUCCESS;
278 }
279
280 #if 0
281 /* simple write routine that does not use run length encoding */
282
283 /* These routines can be made faster by allocating a larger buffer and
284    fread-ing and fwrite-ing the data in larger chunks */
285 static int rgbe_write_pixels(FILE * fp, float *data, int numpixels)
286 {
287         unsigned char rgbe[4];
288
289         while(numpixels-- > 0) {
290                 float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
291                 data += RGBE_DATA_SIZE;
292                 if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
293                         return rgbe_error(rgbe_write_error, NULL);
294         }
295         return RGBE_RETURN_SUCCESS;
296 }
297 #endif
298
299 /* simple read routine.  will not correctly handle run length encoding */
300 static int rgbe_read_pixels(struct img_io *io, float *data, int numpixels)
301 {
302         unsigned char rgbe[4];
303
304         while(numpixels-- > 0) {
305                 if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1)
306                         return rgbe_error(rgbe_read_error, NULL);
307                 rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
308                 data += RGBE_DATA_SIZE;
309         }
310         return RGBE_RETURN_SUCCESS;
311 }
312
313 #if 0
314 /* The code below is only needed for the run-length encoded files. */
315
316 /* Run length encoding adds considerable complexity but does */
317
318 /* save some space.  For each scanline, each channel (r,g,b,e) is */
319
320 /* encoded separately for better compression. */
321
322 static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes)
323 {
324 #define MINRUNLENGTH 4
325         int cur, beg_run, run_count, old_run_count, nonrun_count;
326         unsigned char buf[2];
327
328         cur = 0;
329         while(cur < numbytes) {
330                 beg_run = cur;
331                 /* find next run of length at least 4 if one exists */
332                 run_count = old_run_count = 0;
333                 while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
334                         beg_run += run_count;
335                         old_run_count = run_count;
336                         run_count = 1;
337                         while((beg_run + run_count < numbytes) && (run_count < 127)
338                                   && (data[beg_run] == data[beg_run + run_count]))
339                                 run_count++;
340                 }
341                 /* if data before next big run is a short run then write it as such */
342                 if((old_run_count > 1) && (old_run_count == beg_run - cur)) {
343                         buf[0] = 128 + old_run_count;   /*write short run */
344                         buf[1] = data[cur];
345                         if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
346                                 return rgbe_error(rgbe_write_error, NULL);
347                         cur = beg_run;
348                 }
349                 /* write out bytes until we reach the start of the next run */
350                 while(cur < beg_run) {
351                         nonrun_count = beg_run - cur;
352                         if(nonrun_count > 128)
353                                 nonrun_count = 128;
354                         buf[0] = nonrun_count;
355                         if(fwrite(buf, sizeof(buf[0]), 1, fp) < 1)
356                                 return rgbe_error(rgbe_write_error, NULL);
357                         if(fwrite(&data[cur], sizeof(data[0]) * nonrun_count, 1, fp) < 1)
358                                 return rgbe_error(rgbe_write_error, NULL);
359                         cur += nonrun_count;
360                 }
361                 /* write out next run if one was found */
362                 if(run_count >= MINRUNLENGTH) {
363                         buf[0] = 128 + run_count;
364                         buf[1] = data[beg_run];
365                         if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
366                                 return rgbe_error(rgbe_write_error, NULL);
367                         cur += run_count;
368                 }
369         }
370         return RGBE_RETURN_SUCCESS;
371 #undef MINRUNLENGTH
372 }
373
374 static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
375 {
376         unsigned char rgbe[4];
377         unsigned char *buffer;
378         int i, err;
379
380         if((scanline_width < 8) || (scanline_width > 0x7fff))
381                 /* run length encoding is not allowed so write flat */
382                 return rgbe_write_pixels(io, data, scanline_width * num_scanlines);
383         buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width);
384         if(buffer == NULL)
385                 /* no buffer space so write flat */
386                 return rgbe_write_pixels(fp, data, scanline_width * num_scanlines);
387         while(num_scanlines-- > 0) {
388                 rgbe[0] = 2;
389                 rgbe[1] = 2;
390                 rgbe[2] = scanline_width >> 8;
391                 rgbe[3] = scanline_width & 0xFF;
392                 if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
393                         free(buffer);
394                         return rgbe_error(rgbe_write_error, NULL);
395                 }
396                 for(i = 0; i < scanline_width; i++) {
397                         float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
398                         buffer[i] = rgbe[0];
399                         buffer[i + scanline_width] = rgbe[1];
400                         buffer[i + 2 * scanline_width] = rgbe[2];
401                         buffer[i + 3 * scanline_width] = rgbe[3];
402                         data += RGBE_DATA_SIZE;
403                 }
404                 /* write out each of the four channels separately run length encoded */
405                 /* first red, then green, then blue, then exponent */
406                 for(i = 0; i < 4; i++) {
407                         if((err = rgbe_write_bytes_rle(fp, &buffer[i * scanline_width],
408                                                                                   scanline_width)) != RGBE_RETURN_SUCCESS) {
409                                 free(buffer);
410                                 return err;
411                         }
412                 }
413         }
414         free(buffer);
415         return RGBE_RETURN_SUCCESS;
416 }
417 #endif
418
419 static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
420 {
421         unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
422         int i, count;
423         unsigned char buf[2];
424
425         if((scanline_width < 8) || (scanline_width > 0x7fff))
426                 /* run length encoding is not allowed so read flat */
427                 return rgbe_read_pixels(io, data, scanline_width * num_scanlines);
428         scanline_buffer = NULL;
429         /* read in each successive scanline */
430         while(num_scanlines > 0) {
431                 if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) {
432                         free(scanline_buffer);
433                         return rgbe_error(rgbe_read_error, NULL);
434                 }
435                 if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
436                         /* this file is not run length encoded */
437                         rgbe2float(&data[0], &data[1], &data[2], rgbe);
438                         data += RGBE_DATA_SIZE;
439                         free(scanline_buffer);
440                         return rgbe_read_pixels(io, data, scanline_width * num_scanlines - 1);
441                 }
442                 if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
443                         free(scanline_buffer);
444                         return rgbe_error(rgbe_format_error, "wrong scanline width");
445                 }
446                 if(scanline_buffer == NULL)
447                         scanline_buffer = (unsigned char *)
448                                 malloc(sizeof(unsigned char) * 4 * scanline_width);
449                 if(scanline_buffer == NULL)
450                         return rgbe_error(rgbe_memory_error, "unable to allocate buffer space");
451
452                 ptr = &scanline_buffer[0];
453                 /* read each of the four channels for the scanline into the buffer */
454                 for(i = 0; i < 4; i++) {
455                         ptr_end = &scanline_buffer[(i + 1) * scanline_width];
456                         while(ptr < ptr_end) {
457                                 if(io->read(buf, sizeof(buf[0]) * 2, io->uptr) < 1) {
458                                         free(scanline_buffer);
459                                         return rgbe_error(rgbe_read_error, NULL);
460                                 }
461                                 if(buf[0] > 128) {
462                                         /* a run of the same value */
463                                         count = buf[0] - 128;
464                                         if((count == 0) || (count > ptr_end - ptr)) {
465                                                 free(scanline_buffer);
466                                                 return rgbe_error(rgbe_format_error, "bad scanline data");
467                                         }
468                                         while(count-- > 0)
469                                                 *ptr++ = buf[1];
470                                 } else {
471                                         /* a non-run */
472                                         count = buf[0];
473                                         if((count == 0) || (count > ptr_end - ptr)) {
474                                                 free(scanline_buffer);
475                                                 return rgbe_error(rgbe_format_error, "bad scanline data");
476                                         }
477                                         *ptr++ = buf[1];
478                                         if(--count > 0) {
479                                                 if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) {
480                                                         free(scanline_buffer);
481                                                         return rgbe_error(rgbe_read_error, NULL);
482                                                 }
483                                                 ptr += count;
484                                         }
485                                 }
486                         }
487                 }
488                 /* now convert data from buffer into floats */
489                 for(i = 0; i < scanline_width; i++) {
490                         rgbe[0] = scanline_buffer[i];
491                         rgbe[1] = scanline_buffer[i + scanline_width];
492                         rgbe[2] = scanline_buffer[i + 2 * scanline_width];
493                         rgbe[3] = scanline_buffer[i + 3 * scanline_width];
494                         rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
495                         data += RGBE_DATA_SIZE;
496                 }
497                 num_scanlines--;
498         }
499         free(scanline_buffer);
500         return RGBE_RETURN_SUCCESS;
501 }