nuclear@10: /* This file contains code to read and write four byte rgbe file format nuclear@10: * developed by Greg Ward. It handles the conversions between rgbe and nuclear@10: * pixels consisting of floats. The data is assumed to be an array of floats. nuclear@10: * By default there are three floats per pixel in the order red, green, blue. nuclear@10: * (RGBE_DATA_??? values control this.) nuclear@10: * nuclear@10: * written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 nuclear@10: * based on code written by Greg Ward nuclear@10: * minor modifications by John Tsiombikas (nuclear@member.fsf.org) apr.9 2007 nuclear@10: */ nuclear@10: nuclear@10: #include nuclear@10: #include nuclear@10: #include nuclear@10: #include nuclear@10: #include nuclear@10: #include nuclear@10: #include "imago2.h" nuclear@10: #include "ftype_module.h" nuclear@10: nuclear@10: nuclear@10: typedef struct { nuclear@10: int valid; /* indicate which fields are valid */ nuclear@10: char programtype[16]; /* listed at beginning of file to identify it nuclear@10: * after "#?". defaults to "RGBE" */ nuclear@10: float gamma; /* image has already been gamma corrected with nuclear@10: * given gamma. defaults to 1.0 (no correction) */ nuclear@10: float exposure; /* a value of 1.0 in an image corresponds to nuclear@10: * watts/steradian/m^2. nuclear@10: * defaults to 1.0 */ nuclear@10: } rgbe_header_info; nuclear@10: nuclear@10: nuclear@10: static int check(struct img_io *io); nuclear@10: static int read(struct img_pixmap *img, struct img_io *io); nuclear@10: static int write(struct img_pixmap *img, struct img_io *io); nuclear@10: nuclear@10: static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info); nuclear@10: static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines); nuclear@10: nuclear@10: nuclear@10: int img_register_rgbe(void) nuclear@10: { nuclear@10: static struct ftype_module mod = {".rgbe", check, read, write}; nuclear@10: return img_register_module(&mod); nuclear@10: } nuclear@10: nuclear@10: nuclear@10: static int check(struct img_io *io) nuclear@10: { nuclear@10: int xsz, ysz, res; nuclear@10: long pos = io->seek(0, SEEK_CUR, io->uptr); nuclear@10: nuclear@10: rgbe_header_info hdr; nuclear@10: res = rgbe_read_header(io, &xsz, &ysz, &hdr); nuclear@10: nuclear@10: io->seek(pos, SEEK_SET, io->uptr); nuclear@10: return res; nuclear@10: } nuclear@10: nuclear@10: static int read(struct img_pixmap *img, struct img_io *io) nuclear@10: { nuclear@10: int xsz, ysz; nuclear@10: rgbe_header_info hdr; nuclear@10: nuclear@10: if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) { nuclear@10: return -1; nuclear@10: } nuclear@10: nuclear@10: if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) { nuclear@10: return -1; nuclear@10: } nuclear@10: if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) { nuclear@10: return -1; nuclear@10: } nuclear@10: return 0; nuclear@10: } nuclear@10: nuclear@10: static int write(struct img_pixmap *img, struct img_io *io) nuclear@10: { nuclear@10: return -1; /* TODO */ nuclear@10: } nuclear@10: nuclear@10: nuclear@10: static int iofgetc(struct img_io *io) nuclear@10: { nuclear@10: char c; nuclear@10: return io->read(&c, 1, io->uptr) < 1 ? -1 : c; nuclear@10: } nuclear@10: nuclear@10: static char *iofgets(char *buf, int size, struct img_io *io) nuclear@10: { nuclear@10: int c; nuclear@10: char *ptr = buf; nuclear@10: nuclear@10: while(--size > 0 && (c = iofgetc(io)) != -1) { nuclear@10: *ptr++ = c; nuclear@10: if(c == '\n') break; nuclear@10: } nuclear@10: *ptr = 0; nuclear@10: nuclear@10: return ptr == buf ? 0 : buf; nuclear@10: } nuclear@10: nuclear@10: nuclear@10: /* flags indicating which fields in an rgbe_header_info are valid */ nuclear@10: #define RGBE_VALID_PROGRAMTYPE 0x01 nuclear@10: #define RGBE_VALID_GAMMA 0x02 nuclear@10: #define RGBE_VALID_EXPOSURE 0x04 nuclear@10: nuclear@10: /* return codes for rgbe routines */ nuclear@10: #define RGBE_RETURN_SUCCESS 0 nuclear@10: #define RGBE_RETURN_FAILURE -1 nuclear@10: nuclear@10: nuclear@10: #if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L nuclear@10: #define INLINE inline nuclear@10: #else nuclear@10: #define INLINE nuclear@10: #endif nuclear@10: nuclear@10: /* offsets to red, green, and blue components in a data (float) pixel */ nuclear@10: #define RGBE_DATA_RED 0 nuclear@10: #define RGBE_DATA_GREEN 1 nuclear@10: #define RGBE_DATA_BLUE 2 nuclear@10: nuclear@10: /* number of floats per pixel */ nuclear@10: #define RGBE_DATA_SIZE 3 nuclear@10: nuclear@10: enum rgbe_error_codes { nuclear@10: rgbe_read_error, nuclear@10: rgbe_write_error, nuclear@10: rgbe_format_error, nuclear@10: rgbe_memory_error nuclear@10: }; nuclear@10: nuclear@10: nuclear@10: /* default error routine. change this to change error handling */ nuclear@10: static int rgbe_error(int rgbe_error_code, char *msg) nuclear@10: { nuclear@10: switch (rgbe_error_code) { nuclear@10: case rgbe_read_error: nuclear@10: fprintf(stderr, "RGBE read error: %s\n", strerror(errno)); nuclear@10: break; nuclear@10: nuclear@10: case rgbe_write_error: nuclear@10: fprintf(stderr, "RGBE write error: %s\n", strerror(errno)); nuclear@10: break; nuclear@10: nuclear@10: case rgbe_format_error: nuclear@10: fprintf(stderr, "RGBE bad file format: %s\n", msg); nuclear@10: break; nuclear@10: nuclear@10: default: nuclear@10: case rgbe_memory_error: nuclear@10: fprintf(stderr, "RGBE error: %s\n", msg); nuclear@10: } nuclear@10: return RGBE_RETURN_FAILURE; nuclear@10: } nuclear@10: nuclear@10: /* standard conversion from float pixels to rgbe pixels */ nuclear@10: /*static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue) nuclear@10: { nuclear@10: float v; nuclear@10: int e; nuclear@10: nuclear@10: v = red; nuclear@10: if(green > v) nuclear@10: v = green; nuclear@10: if(blue > v) nuclear@10: v = blue; nuclear@10: if(v < 1e-32) { nuclear@10: rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; nuclear@10: } else { nuclear@10: v = frexp(v, &e) * 256.0 / v; nuclear@10: rgbe[0] = (unsigned char)(red * v); nuclear@10: rgbe[1] = (unsigned char)(green * v); nuclear@10: rgbe[2] = (unsigned char)(blue * v); nuclear@10: rgbe[3] = (unsigned char)(e + 128); nuclear@10: } nuclear@10: }*/ nuclear@10: nuclear@10: /* standard conversion from rgbe to float pixels */ nuclear@10: /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */ nuclear@10: /* in the range [0,1] to map back into the range [0,1]. */ nuclear@10: static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]) nuclear@10: { nuclear@10: float f; nuclear@10: nuclear@10: if(rgbe[3]) { /*nonzero pixel */ nuclear@10: f = ldexp(1.0, rgbe[3] - (int)(128 + 8)); nuclear@10: *red = rgbe[0] * f; nuclear@10: *green = rgbe[1] * f; nuclear@10: *blue = rgbe[2] * f; nuclear@10: } else nuclear@10: *red = *green = *blue = 0.0; nuclear@10: } nuclear@10: nuclear@10: #if 0 nuclear@10: /* default minimal header. modify if you want more information in header */ nuclear@10: static int rgbe_write_header(FILE * fp, int width, int height, rgbe_header_info * info) nuclear@10: { nuclear@10: char *programtype = "RGBE"; nuclear@10: nuclear@10: if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) nuclear@10: programtype = info->programtype; nuclear@10: if(fprintf(fp, "#?%s\n", programtype) < 0) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: /* The #? is to identify file type, the programtype is optional. */ nuclear@10: if(info && (info->valid & RGBE_VALID_GAMMA)) { nuclear@10: if(fprintf(fp, "GAMMA=%g\n", info->gamma) < 0) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: } nuclear@10: if(info && (info->valid & RGBE_VALID_EXPOSURE)) { nuclear@10: if(fprintf(fp, "EXPOSURE=%g\n", info->exposure) < 0) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: } nuclear@10: if(fprintf(fp, "FORMAT=32-bit_rle_rgbe\n\n") < 0) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: if(fprintf(fp, "-Y %d +X %d\n", height, width) < 0) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: } nuclear@10: #endif nuclear@10: nuclear@10: /* minimal header reading. modify if you want to parse more information */ nuclear@10: static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info) nuclear@10: { nuclear@10: char buf[128]; nuclear@10: float tempf; nuclear@10: int i; nuclear@10: nuclear@10: if(info) { nuclear@10: info->valid = 0; nuclear@10: info->programtype[0] = 0; nuclear@10: info->gamma = info->exposure = 1.0; nuclear@10: } nuclear@10: if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ nuclear@10: if((buf[0] != '#') || (buf[1] != '?')) { nuclear@10: /* if you want to require the magic token then uncomment the next line */ nuclear@10: /*return rgbe_error(rgbe_format_error,"bad initial token"); */ nuclear@10: } else if(info) { nuclear@10: info->valid |= RGBE_VALID_PROGRAMTYPE; nuclear@10: for(i = 0; i < sizeof(info->programtype) - 1; i++) { nuclear@10: if((buf[i + 2] == 0) || isspace(buf[i + 2])) nuclear@10: break; nuclear@10: info->programtype[i] = buf[i + 2]; nuclear@10: } nuclear@10: info->programtype[i] = 0; nuclear@10: if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) nuclear@10: return rgbe_error(rgbe_read_error, NULL); nuclear@10: } nuclear@10: for(;;) { nuclear@10: if((buf[0] == 0) || (buf[0] == '\n')) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "no FORMAT specifier found");*/ nuclear@10: else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0) nuclear@10: break; /* format found so break out of loop */ nuclear@10: else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) { nuclear@10: info->gamma = tempf; nuclear@10: info->valid |= RGBE_VALID_GAMMA; nuclear@10: } else if(info && (sscanf(buf, "EXPOSURE=%g", &tempf) == 1)) { nuclear@10: info->exposure = tempf; nuclear@10: info->valid |= RGBE_VALID_EXPOSURE; nuclear@10: } nuclear@10: if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ nuclear@10: } nuclear@10: if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ nuclear@10: if(strcmp(buf, "\n") != 0) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");*/ nuclear@10: if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ nuclear@10: if(sscanf(buf, "-Y %d +X %d", height, width) < 2) nuclear@10: return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing image size specifier");*/ nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: } nuclear@10: nuclear@10: #if 0 nuclear@10: /* simple write routine that does not use run length encoding */ nuclear@10: nuclear@10: /* These routines can be made faster by allocating a larger buffer and nuclear@10: fread-ing and fwrite-ing the data in larger chunks */ nuclear@10: static int rgbe_write_pixels(FILE * fp, float *data, int numpixels) nuclear@10: { nuclear@10: unsigned char rgbe[4]; nuclear@10: nuclear@10: while(numpixels-- > 0) { nuclear@10: float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]); nuclear@10: data += RGBE_DATA_SIZE; nuclear@10: if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: } nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: } nuclear@10: #endif nuclear@10: nuclear@10: /* simple read routine. will not correctly handle run length encoding */ nuclear@10: static int rgbe_read_pixels(struct img_io *io, float *data, int numpixels) nuclear@10: { nuclear@10: unsigned char rgbe[4]; nuclear@10: nuclear@10: while(numpixels-- > 0) { nuclear@10: if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) nuclear@10: return rgbe_error(rgbe_read_error, NULL); nuclear@10: rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe); nuclear@10: data += RGBE_DATA_SIZE; nuclear@10: } nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: } nuclear@10: nuclear@10: #if 0 nuclear@10: /* The code below is only needed for the run-length encoded files. */ nuclear@10: nuclear@10: /* Run length encoding adds considerable complexity but does */ nuclear@10: nuclear@10: /* save some space. For each scanline, each channel (r,g,b,e) is */ nuclear@10: nuclear@10: /* encoded separately for better compression. */ nuclear@10: nuclear@10: static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes) nuclear@10: { nuclear@10: #define MINRUNLENGTH 4 nuclear@10: int cur, beg_run, run_count, old_run_count, nonrun_count; nuclear@10: unsigned char buf[2]; nuclear@10: nuclear@10: cur = 0; nuclear@10: while(cur < numbytes) { nuclear@10: beg_run = cur; nuclear@10: /* find next run of length at least 4 if one exists */ nuclear@10: run_count = old_run_count = 0; nuclear@10: while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { nuclear@10: beg_run += run_count; nuclear@10: old_run_count = run_count; nuclear@10: run_count = 1; nuclear@10: while((beg_run + run_count < numbytes) && (run_count < 127) nuclear@10: && (data[beg_run] == data[beg_run + run_count])) nuclear@10: run_count++; nuclear@10: } nuclear@10: /* if data before next big run is a short run then write it as such */ nuclear@10: if((old_run_count > 1) && (old_run_count == beg_run - cur)) { nuclear@10: buf[0] = 128 + old_run_count; /*write short run */ nuclear@10: buf[1] = data[cur]; nuclear@10: if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: cur = beg_run; nuclear@10: } nuclear@10: /* write out bytes until we reach the start of the next run */ nuclear@10: while(cur < beg_run) { nuclear@10: nonrun_count = beg_run - cur; nuclear@10: if(nonrun_count > 128) nuclear@10: nonrun_count = 128; nuclear@10: buf[0] = nonrun_count; nuclear@10: if(fwrite(buf, sizeof(buf[0]), 1, fp) < 1) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: if(fwrite(&data[cur], sizeof(data[0]) * nonrun_count, 1, fp) < 1) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: cur += nonrun_count; nuclear@10: } nuclear@10: /* write out next run if one was found */ nuclear@10: if(run_count >= MINRUNLENGTH) { nuclear@10: buf[0] = 128 + run_count; nuclear@10: buf[1] = data[beg_run]; nuclear@10: if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1) nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: cur += run_count; nuclear@10: } nuclear@10: } nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: #undef MINRUNLENGTH nuclear@10: } nuclear@10: nuclear@10: static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines) nuclear@10: { nuclear@10: unsigned char rgbe[4]; nuclear@10: unsigned char *buffer; nuclear@10: int i, err; nuclear@10: nuclear@10: if((scanline_width < 8) || (scanline_width > 0x7fff)) nuclear@10: /* run length encoding is not allowed so write flat */ nuclear@10: return rgbe_write_pixels(io, data, scanline_width * num_scanlines); nuclear@10: buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width); nuclear@10: if(buffer == NULL) nuclear@10: /* no buffer space so write flat */ nuclear@10: return rgbe_write_pixels(fp, data, scanline_width * num_scanlines); nuclear@10: while(num_scanlines-- > 0) { nuclear@10: rgbe[0] = 2; nuclear@10: rgbe[1] = 2; nuclear@10: rgbe[2] = scanline_width >> 8; nuclear@10: rgbe[3] = scanline_width & 0xFF; nuclear@10: if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) { nuclear@10: free(buffer); nuclear@10: return rgbe_error(rgbe_write_error, NULL); nuclear@10: } nuclear@10: for(i = 0; i < scanline_width; i++) { nuclear@10: float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]); nuclear@10: buffer[i] = rgbe[0]; nuclear@10: buffer[i + scanline_width] = rgbe[1]; nuclear@10: buffer[i + 2 * scanline_width] = rgbe[2]; nuclear@10: buffer[i + 3 * scanline_width] = rgbe[3]; nuclear@10: data += RGBE_DATA_SIZE; nuclear@10: } nuclear@10: /* write out each of the four channels separately run length encoded */ nuclear@10: /* first red, then green, then blue, then exponent */ nuclear@10: for(i = 0; i < 4; i++) { nuclear@10: if((err = rgbe_write_bytes_rle(fp, &buffer[i * scanline_width], nuclear@10: scanline_width)) != RGBE_RETURN_SUCCESS) { nuclear@10: free(buffer); nuclear@10: return err; nuclear@10: } nuclear@10: } nuclear@10: } nuclear@10: free(buffer); nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: } nuclear@10: #endif nuclear@10: nuclear@10: static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines) nuclear@10: { nuclear@10: unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end; nuclear@10: int i, count; nuclear@10: unsigned char buf[2]; nuclear@10: nuclear@10: if((scanline_width < 8) || (scanline_width > 0x7fff)) nuclear@10: /* run length encoding is not allowed so read flat */ nuclear@10: return rgbe_read_pixels(io, data, scanline_width * num_scanlines); nuclear@10: scanline_buffer = NULL; nuclear@10: /* read in each successive scanline */ nuclear@10: while(num_scanlines > 0) { nuclear@10: if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) { nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_error(rgbe_read_error, NULL); nuclear@10: } nuclear@10: if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) { nuclear@10: /* this file is not run length encoded */ nuclear@10: rgbe2float(&data[0], &data[1], &data[2], rgbe); nuclear@10: data += RGBE_DATA_SIZE; nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_read_pixels(io, data, scanline_width * num_scanlines - 1); nuclear@10: } nuclear@10: if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) { nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_error(rgbe_format_error, "wrong scanline width"); nuclear@10: } nuclear@10: if(scanline_buffer == NULL) nuclear@10: scanline_buffer = (unsigned char *) nuclear@10: malloc(sizeof(unsigned char) * 4 * scanline_width); nuclear@10: if(scanline_buffer == NULL) nuclear@10: return rgbe_error(rgbe_memory_error, "unable to allocate buffer space"); nuclear@10: nuclear@10: ptr = &scanline_buffer[0]; nuclear@10: /* read each of the four channels for the scanline into the buffer */ nuclear@10: for(i = 0; i < 4; i++) { nuclear@10: ptr_end = &scanline_buffer[(i + 1) * scanline_width]; nuclear@10: while(ptr < ptr_end) { nuclear@10: if(io->read(buf, sizeof(buf[0]) * 2, io->uptr) < 1) { nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_error(rgbe_read_error, NULL); nuclear@10: } nuclear@10: if(buf[0] > 128) { nuclear@10: /* a run of the same value */ nuclear@10: count = buf[0] - 128; nuclear@10: if((count == 0) || (count > ptr_end - ptr)) { nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_error(rgbe_format_error, "bad scanline data"); nuclear@10: } nuclear@10: while(count-- > 0) nuclear@10: *ptr++ = buf[1]; nuclear@10: } else { nuclear@10: /* a non-run */ nuclear@10: count = buf[0]; nuclear@10: if((count == 0) || (count > ptr_end - ptr)) { nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_error(rgbe_format_error, "bad scanline data"); nuclear@10: } nuclear@10: *ptr++ = buf[1]; nuclear@10: if(--count > 0) { nuclear@10: if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) { nuclear@10: free(scanline_buffer); nuclear@10: return rgbe_error(rgbe_read_error, NULL); nuclear@10: } nuclear@10: ptr += count; nuclear@10: } nuclear@10: } nuclear@10: } nuclear@10: } nuclear@10: /* now convert data from buffer into floats */ nuclear@10: for(i = 0; i < scanline_width; i++) { nuclear@10: rgbe[0] = scanline_buffer[i]; nuclear@10: rgbe[1] = scanline_buffer[i + scanline_width]; nuclear@10: rgbe[2] = scanline_buffer[i + 2 * scanline_width]; nuclear@10: rgbe[3] = scanline_buffer[i + 3 * scanline_width]; nuclear@10: rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe); nuclear@10: data += RGBE_DATA_SIZE; nuclear@10: } nuclear@10: num_scanlines--; nuclear@10: } nuclear@10: free(scanline_buffer); nuclear@10: return RGBE_RETURN_SUCCESS; nuclear@10: }