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