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.)
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
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.
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);
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);
44 int img_register_rgbe(void)
46 static struct ftype_module mod = {".rgbe:.pic:.hdr", check, read, write};
47 return img_register_module(&mod);
51 static int check(struct img_io *io)
54 long pos = io->seek(0, SEEK_CUR, io->uptr);
57 res = rgbe_read_header(io, &xsz, &ysz, &hdr);
59 io->seek(pos, SEEK_SET, io->uptr);
63 static int read(struct img_pixmap *img, struct img_io *io)
68 if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) {
72 if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) {
75 if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) {
81 static int write(struct img_pixmap *img, struct img_io *io)
83 struct img_pixmap fimg;
86 if(img_copy(&fimg, img) == -1) {
90 if(img_convert(&fimg, IMG_FMT_RGBF) == -1) {
95 if(rgbe_write_header(io, fimg.width, fimg.height, 0) == -1) {
99 if(rgbe_write_pixels_rle(io, fimg.pixels, fimg.width, fimg.height) == -1) {
108 static int iofgetc(struct img_io *io)
111 return io->read(&c, 1, io->uptr) < 1 ? -1 : c;
114 static char *iofgets(char *buf, int size, struct img_io *io)
119 while(--size > 0 && (c = iofgetc(io)) != -1) {
125 return ptr == buf ? 0 : buf;
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
134 /* return codes for rgbe routines */
135 #define RGBE_RETURN_SUCCESS 0
136 #define RGBE_RETURN_FAILURE -1
139 #if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L
140 #define INLINE inline
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
150 /* number of floats per pixel */
151 #define RGBE_DATA_SIZE 3
153 enum rgbe_error_codes {
161 /* default error routine. change this to change error handling */
162 static int rgbe_error(int rgbe_error_code, char *msg)
164 switch (rgbe_error_code) {
165 case rgbe_read_error:
166 fprintf(stderr, "RGBE read error: %s\n", strerror(errno));
169 case rgbe_write_error:
170 fprintf(stderr, "RGBE write error: %s\n", strerror(errno));
173 case rgbe_format_error:
174 fprintf(stderr, "RGBE bad file format: %s\n", msg);
178 case rgbe_memory_error:
179 fprintf(stderr, "RGBE error: %s\n", msg);
181 return RGBE_RETURN_FAILURE;
184 /* standard conversion from float pixels to rgbe pixels */
185 static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
196 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
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);
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])
213 if(rgbe[3]) { /*nonzero pixel */
214 f = ldexp(1.0, rgbe[3] - (int)(128 + 8));
216 *green = rgbe[1] * f;
219 *red = *green = *blue = 0.0;
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)
227 const char *programtype = "RGBE";
229 if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) {
230 programtype = info->programtype;
231 ptypelen = strlen(programtype);
233 buf = malloc(ptypelen > 120 ? ptypelen + 8 : 128);
234 sprintf(buf, "#?%s\n", programtype);
235 if(io->write(buf, strlen(buf), io->uptr) <= 0)
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)
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)
248 strcpy(buf, "FORMAT=32-bit_rle_rgbe\n\n");
249 if(io->write(buf, strlen(buf), io->uptr) <= 0)
251 sprintf(buf, "-Y %d +X %d\n", height, width);
252 if(io->write(buf, strlen(buf), io->uptr) <= 0)
256 return RGBE_RETURN_SUCCESS;
259 return rgbe_error(rgbe_write_error, NULL);
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)
267 int i, fmt_found = 0;
271 info->programtype[0] = 0;
272 info->gamma = info->exposure = 1.0;
275 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL) {
276 return RGBE_RETURN_FAILURE;
279 if((buf[0] != '#') || (buf[1] != '?')) {
280 return RGBE_RETURN_FAILURE;
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])) {
287 info->programtype[i] = buf[i + 2];
289 info->programtype[i] = 0;
290 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) {
291 return RGBE_RETURN_FAILURE;
294 while(buf[0] && buf[0] != '\n') {
295 if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0) {
297 } else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) {
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;
304 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) {
305 return RGBE_RETURN_FAILURE;
309 return RGBE_RETURN_FAILURE;
312 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) {
313 return RGBE_RETURN_FAILURE;
315 if(sscanf(buf, "-Y %d +X %d", height, width) < 2) {
316 return RGBE_RETURN_FAILURE;
318 return RGBE_RETURN_SUCCESS;
321 /* simple write routine that does not use run length encoding */
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)
327 unsigned char rgbe[4];
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);
335 return RGBE_RETURN_SUCCESS;
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)
341 unsigned char rgbe[4];
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;
349 return RGBE_RETURN_SUCCESS;
352 /* The code below is only needed for the run-length encoded files. */
354 /* Run length encoding adds considerable complexity but does */
356 /* save some space. For each scanline, each channel (r,g,b,e) is */
358 /* encoded separately for better compression. */
360 static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes)
362 #define MINRUNLENGTH 4
363 int cur, beg_run, run_count, old_run_count, nonrun_count;
364 unsigned char buf[2];
367 while(cur < numbytes) {
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;
375 while((beg_run + run_count < numbytes) && (run_count < 127)
376 && (data[beg_run] == data[beg_run + run_count]))
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 */
383 if(io->write(buf, sizeof(buf[0]) * 2, io->uptr) < 1)
384 return rgbe_error(rgbe_write_error, NULL);
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)
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);
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);
408 return RGBE_RETURN_SUCCESS;
412 static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
414 unsigned char rgbe[4];
415 unsigned char *buffer;
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);
423 /* no buffer space so write flat */
424 return rgbe_write_pixels(io, data, scanline_width * num_scanlines);
425 while(num_scanlines-- > 0) {
428 rgbe[2] = scanline_width >> 8;
429 rgbe[3] = scanline_width & 0xFF;
430 if(io->write(rgbe, sizeof(rgbe), io->uptr) < 1) {
432 return rgbe_error(rgbe_write_error, NULL);
434 for(i = 0; i < scanline_width; i++) {
435 float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
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;
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) {
453 return RGBE_RETURN_SUCCESS;
456 static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
458 unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
460 unsigned char buf[2];
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);
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);
479 if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
480 free(scanline_buffer);
481 return rgbe_error(rgbe_format_error, "wrong scanline width");
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");
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);
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");
510 if((count == 0) || (count > ptr_end - ptr)) {
511 free(scanline_buffer);
512 return rgbe_error(rgbe_format_error, "bad scanline data");
516 if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) {
517 free(scanline_buffer);
518 return rgbe_error(rgbe_read_error, NULL);
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;
536 free(scanline_buffer);
537 return RGBE_RETURN_SUCCESS;