vrshoot
diff libs/imago/file_rgbe.c @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/imago/file_rgbe.c Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,501 @@ 1.4 +/* This file contains code to read and write four byte rgbe file format 1.5 + * developed by Greg Ward. It handles the conversions between rgbe and 1.6 + * pixels consisting of floats. The data is assumed to be an array of floats. 1.7 + * By default there are three floats per pixel in the order red, green, blue. 1.8 + * (RGBE_DATA_??? values control this.) 1.9 + * 1.10 + * written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 1.11 + * based on code written by Greg Ward 1.12 + * minor modifications by John Tsiombikas (nuclear@member.fsf.org) apr.9 2007 1.13 + */ 1.14 + 1.15 +#include <stdio.h> 1.16 +#include <stdlib.h> 1.17 +#include <string.h> 1.18 +#include <math.h> 1.19 +#include <ctype.h> 1.20 +#include <errno.h> 1.21 +#include "imago2.h" 1.22 +#include "ftype_module.h" 1.23 + 1.24 + 1.25 +typedef struct { 1.26 + int valid; /* indicate which fields are valid */ 1.27 + char programtype[16]; /* listed at beginning of file to identify it 1.28 + * after "#?". defaults to "RGBE" */ 1.29 + float gamma; /* image has already been gamma corrected with 1.30 + * given gamma. defaults to 1.0 (no correction) */ 1.31 + float exposure; /* a value of 1.0 in an image corresponds to 1.32 + * <exposure> watts/steradian/m^2. 1.33 + * defaults to 1.0 */ 1.34 +} rgbe_header_info; 1.35 + 1.36 + 1.37 +static int check(struct img_io *io); 1.38 +static int read(struct img_pixmap *img, struct img_io *io); 1.39 +static int write(struct img_pixmap *img, struct img_io *io); 1.40 + 1.41 +static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info); 1.42 +static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines); 1.43 + 1.44 + 1.45 +int img_register_rgbe(void) 1.46 +{ 1.47 + static struct ftype_module mod = {".rgbe", check, read, write}; 1.48 + return img_register_module(&mod); 1.49 +} 1.50 + 1.51 + 1.52 +static int check(struct img_io *io) 1.53 +{ 1.54 + int xsz, ysz, res; 1.55 + long pos = io->seek(0, SEEK_CUR, io->uptr); 1.56 + 1.57 + rgbe_header_info hdr; 1.58 + res = rgbe_read_header(io, &xsz, &ysz, &hdr); 1.59 + 1.60 + io->seek(pos, SEEK_SET, io->uptr); 1.61 + return res; 1.62 +} 1.63 + 1.64 +static int read(struct img_pixmap *img, struct img_io *io) 1.65 +{ 1.66 + int xsz, ysz; 1.67 + rgbe_header_info hdr; 1.68 + 1.69 + if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) { 1.70 + return -1; 1.71 + } 1.72 + 1.73 + if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) { 1.74 + return -1; 1.75 + } 1.76 + if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) { 1.77 + return -1; 1.78 + } 1.79 + return 0; 1.80 +} 1.81 + 1.82 +static int write(struct img_pixmap *img, struct img_io *io) 1.83 +{ 1.84 + return -1; /* TODO */ 1.85 +} 1.86 + 1.87 + 1.88 +static int iofgetc(struct img_io *io) 1.89 +{ 1.90 + char c; 1.91 + return io->read(&c, 1, io->uptr) < 1 ? -1 : c; 1.92 +} 1.93 + 1.94 +static char *iofgets(char *buf, int size, struct img_io *io) 1.95 +{ 1.96 + int c; 1.97 + char *ptr = buf; 1.98 + 1.99 + while(--size > 0 && (c = iofgetc(io)) != -1) { 1.100 + *ptr++ = c; 1.101 + if(c == '\n') break; 1.102 + } 1.103 + *ptr = 0; 1.104 + 1.105 + return ptr == buf ? 0 : buf; 1.106 +} 1.107 + 1.108 + 1.109 +/* flags indicating which fields in an rgbe_header_info are valid */ 1.110 +#define RGBE_VALID_PROGRAMTYPE 0x01 1.111 +#define RGBE_VALID_GAMMA 0x02 1.112 +#define RGBE_VALID_EXPOSURE 0x04 1.113 + 1.114 +/* return codes for rgbe routines */ 1.115 +#define RGBE_RETURN_SUCCESS 0 1.116 +#define RGBE_RETURN_FAILURE -1 1.117 + 1.118 + 1.119 +#if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L 1.120 +#define INLINE inline 1.121 +#else 1.122 +#define INLINE 1.123 +#endif 1.124 + 1.125 +/* offsets to red, green, and blue components in a data (float) pixel */ 1.126 +#define RGBE_DATA_RED 0 1.127 +#define RGBE_DATA_GREEN 1 1.128 +#define RGBE_DATA_BLUE 2 1.129 + 1.130 +/* number of floats per pixel */ 1.131 +#define RGBE_DATA_SIZE 3 1.132 + 1.133 +enum rgbe_error_codes { 1.134 + rgbe_read_error, 1.135 + rgbe_write_error, 1.136 + rgbe_format_error, 1.137 + rgbe_memory_error 1.138 +}; 1.139 + 1.140 + 1.141 +/* default error routine. change this to change error handling */ 1.142 +static int rgbe_error(int rgbe_error_code, char *msg) 1.143 +{ 1.144 + switch (rgbe_error_code) { 1.145 + case rgbe_read_error: 1.146 + fprintf(stderr, "RGBE read error: %s\n", strerror(errno)); 1.147 + break; 1.148 + 1.149 + case rgbe_write_error: 1.150 + fprintf(stderr, "RGBE write error: %s\n", strerror(errno)); 1.151 + break; 1.152 + 1.153 + case rgbe_format_error: 1.154 + fprintf(stderr, "RGBE bad file format: %s\n", msg); 1.155 + break; 1.156 + 1.157 + default: 1.158 + case rgbe_memory_error: 1.159 + fprintf(stderr, "RGBE error: %s\n", msg); 1.160 + } 1.161 + return RGBE_RETURN_FAILURE; 1.162 +} 1.163 + 1.164 +/* standard conversion from float pixels to rgbe pixels */ 1.165 +/*static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue) 1.166 +{ 1.167 + float v; 1.168 + int e; 1.169 + 1.170 + v = red; 1.171 + if(green > v) 1.172 + v = green; 1.173 + if(blue > v) 1.174 + v = blue; 1.175 + if(v < 1e-32) { 1.176 + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 1.177 + } else { 1.178 + v = frexp(v, &e) * 256.0 / v; 1.179 + rgbe[0] = (unsigned char)(red * v); 1.180 + rgbe[1] = (unsigned char)(green * v); 1.181 + rgbe[2] = (unsigned char)(blue * v); 1.182 + rgbe[3] = (unsigned char)(e + 128); 1.183 + } 1.184 +}*/ 1.185 + 1.186 +/* standard conversion from rgbe to float pixels */ 1.187 +/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */ 1.188 +/* in the range [0,1] to map back into the range [0,1]. */ 1.189 +static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]) 1.190 +{ 1.191 + float f; 1.192 + 1.193 + if(rgbe[3]) { /*nonzero pixel */ 1.194 + f = ldexp(1.0, rgbe[3] - (int)(128 + 8)); 1.195 + *red = rgbe[0] * f; 1.196 + *green = rgbe[1] * f; 1.197 + *blue = rgbe[2] * f; 1.198 + } else 1.199 + *red = *green = *blue = 0.0; 1.200 +} 1.201 + 1.202 +#if 0 1.203 +/* default minimal header. modify if you want more information in header */ 1.204 +static int rgbe_write_header(FILE * fp, int width, int height, rgbe_header_info * info) 1.205 +{ 1.206 + char *programtype = "RGBE"; 1.207 + 1.208 + if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) 1.209 + programtype = info->programtype; 1.210 + if(fprintf(fp, "#?%s\n", programtype) < 0) 1.211 + return rgbe_error(rgbe_write_error, NULL); 1.212 + /* The #? is to identify file type, the programtype is optional. */ 1.213 + if(info && (info->valid & RGBE_VALID_GAMMA)) { 1.214 + if(fprintf(fp, "GAMMA=%g\n", info->gamma) < 0) 1.215 + return rgbe_error(rgbe_write_error, NULL); 1.216 + } 1.217 + if(info && (info->valid & RGBE_VALID_EXPOSURE)) { 1.218 + if(fprintf(fp, "EXPOSURE=%g\n", info->exposure) < 0) 1.219 + return rgbe_error(rgbe_write_error, NULL); 1.220 + } 1.221 + if(fprintf(fp, "FORMAT=32-bit_rle_rgbe\n\n") < 0) 1.222 + return rgbe_error(rgbe_write_error, NULL); 1.223 + if(fprintf(fp, "-Y %d +X %d\n", height, width) < 0) 1.224 + return rgbe_error(rgbe_write_error, NULL); 1.225 + return RGBE_RETURN_SUCCESS; 1.226 +} 1.227 +#endif 1.228 + 1.229 +/* minimal header reading. modify if you want to parse more information */ 1.230 +static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info) 1.231 +{ 1.232 + char buf[128]; 1.233 + float tempf; 1.234 + int i; 1.235 + 1.236 + if(info) { 1.237 + info->valid = 0; 1.238 + info->programtype[0] = 0; 1.239 + info->gamma = info->exposure = 1.0; 1.240 + } 1.241 + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL) 1.242 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ 1.243 + if((buf[0] != '#') || (buf[1] != '?')) { 1.244 + /* if you want to require the magic token then uncomment the next line */ 1.245 + /*return rgbe_error(rgbe_format_error,"bad initial token"); */ 1.246 + } else if(info) { 1.247 + info->valid |= RGBE_VALID_PROGRAMTYPE; 1.248 + for(i = 0; i < sizeof(info->programtype) - 1; i++) { 1.249 + if((buf[i + 2] == 0) || isspace(buf[i + 2])) 1.250 + break; 1.251 + info->programtype[i] = buf[i + 2]; 1.252 + } 1.253 + info->programtype[i] = 0; 1.254 + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) 1.255 + return rgbe_error(rgbe_read_error, NULL); 1.256 + } 1.257 + for(;;) { 1.258 + if((buf[0] == 0) || (buf[0] == '\n')) 1.259 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "no FORMAT specifier found");*/ 1.260 + else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0) 1.261 + break; /* format found so break out of loop */ 1.262 + else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) { 1.263 + info->gamma = tempf; 1.264 + info->valid |= RGBE_VALID_GAMMA; 1.265 + } else if(info && (sscanf(buf, "EXPOSURE=%g", &tempf) == 1)) { 1.266 + info->exposure = tempf; 1.267 + info->valid |= RGBE_VALID_EXPOSURE; 1.268 + } 1.269 + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) 1.270 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ 1.271 + } 1.272 + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) 1.273 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ 1.274 + if(strcmp(buf, "\n") != 0) 1.275 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");*/ 1.276 + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) 1.277 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ 1.278 + if(sscanf(buf, "-Y %d +X %d", height, width) < 2) 1.279 + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing image size specifier");*/ 1.280 + return RGBE_RETURN_SUCCESS; 1.281 +} 1.282 + 1.283 +#if 0 1.284 +/* simple write routine that does not use run length encoding */ 1.285 + 1.286 +/* These routines can be made faster by allocating a larger buffer and 1.287 + fread-ing and fwrite-ing the data in larger chunks */ 1.288 +static int rgbe_write_pixels(FILE * fp, float *data, int numpixels) 1.289 +{ 1.290 + unsigned char rgbe[4]; 1.291 + 1.292 + while(numpixels-- > 0) { 1.293 + float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]); 1.294 + data += RGBE_DATA_SIZE; 1.295 + if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) 1.296 + return rgbe_error(rgbe_write_error, NULL); 1.297 + } 1.298 + return RGBE_RETURN_SUCCESS; 1.299 +} 1.300 +#endif 1.301 + 1.302 +/* simple read routine. will not correctly handle run length encoding */ 1.303 +static int rgbe_read_pixels(struct img_io *io, float *data, int numpixels) 1.304 +{ 1.305 + unsigned char rgbe[4]; 1.306 + 1.307 + while(numpixels-- > 0) { 1.308 + if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) 1.309 + return rgbe_error(rgbe_read_error, NULL); 1.310 + rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe); 1.311 + data += RGBE_DATA_SIZE; 1.312 + } 1.313 + return RGBE_RETURN_SUCCESS; 1.314 +} 1.315 + 1.316 +#if 0 1.317 +/* The code below is only needed for the run-length encoded files. */ 1.318 + 1.319 +/* Run length encoding adds considerable complexity but does */ 1.320 + 1.321 +/* save some space. For each scanline, each channel (r,g,b,e) is */ 1.322 + 1.323 +/* encoded separately for better compression. */ 1.324 + 1.325 +static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes) 1.326 +{ 1.327 +#define MINRUNLENGTH 4 1.328 + int cur, beg_run, run_count, old_run_count, nonrun_count; 1.329 + unsigned char buf[2]; 1.330 + 1.331 + cur = 0; 1.332 + while(cur < numbytes) { 1.333 + beg_run = cur; 1.334 + /* find next run of length at least 4 if one exists */ 1.335 + run_count = old_run_count = 0; 1.336 + while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { 1.337 + beg_run += run_count; 1.338 + old_run_count = run_count; 1.339 + run_count = 1; 1.340 + while((beg_run + run_count < numbytes) && (run_count < 127) 1.341 + && (data[beg_run] == data[beg_run + run_count])) 1.342 + run_count++; 1.343 + } 1.344 + /* if data before next big run is a short run then write it as such */ 1.345 + if((old_run_count > 1) && (old_run_count == beg_run - cur)) { 1.346 + buf[0] = 128 + old_run_count; /*write short run */ 1.347 + buf[1] = data[cur]; 1.348 + if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1) 1.349 + return rgbe_error(rgbe_write_error, NULL); 1.350 + cur = beg_run; 1.351 + } 1.352 + /* write out bytes until we reach the start of the next run */ 1.353 + while(cur < beg_run) { 1.354 + nonrun_count = beg_run - cur; 1.355 + if(nonrun_count > 128) 1.356 + nonrun_count = 128; 1.357 + buf[0] = nonrun_count; 1.358 + if(fwrite(buf, sizeof(buf[0]), 1, fp) < 1) 1.359 + return rgbe_error(rgbe_write_error, NULL); 1.360 + if(fwrite(&data[cur], sizeof(data[0]) * nonrun_count, 1, fp) < 1) 1.361 + return rgbe_error(rgbe_write_error, NULL); 1.362 + cur += nonrun_count; 1.363 + } 1.364 + /* write out next run if one was found */ 1.365 + if(run_count >= MINRUNLENGTH) { 1.366 + buf[0] = 128 + run_count; 1.367 + buf[1] = data[beg_run]; 1.368 + if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1) 1.369 + return rgbe_error(rgbe_write_error, NULL); 1.370 + cur += run_count; 1.371 + } 1.372 + } 1.373 + return RGBE_RETURN_SUCCESS; 1.374 +#undef MINRUNLENGTH 1.375 +} 1.376 + 1.377 +static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines) 1.378 +{ 1.379 + unsigned char rgbe[4]; 1.380 + unsigned char *buffer; 1.381 + int i, err; 1.382 + 1.383 + if((scanline_width < 8) || (scanline_width > 0x7fff)) 1.384 + /* run length encoding is not allowed so write flat */ 1.385 + return rgbe_write_pixels(io, data, scanline_width * num_scanlines); 1.386 + buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width); 1.387 + if(buffer == NULL) 1.388 + /* no buffer space so write flat */ 1.389 + return rgbe_write_pixels(fp, data, scanline_width * num_scanlines); 1.390 + while(num_scanlines-- > 0) { 1.391 + rgbe[0] = 2; 1.392 + rgbe[1] = 2; 1.393 + rgbe[2] = scanline_width >> 8; 1.394 + rgbe[3] = scanline_width & 0xFF; 1.395 + if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) { 1.396 + free(buffer); 1.397 + return rgbe_error(rgbe_write_error, NULL); 1.398 + } 1.399 + for(i = 0; i < scanline_width; i++) { 1.400 + float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]); 1.401 + buffer[i] = rgbe[0]; 1.402 + buffer[i + scanline_width] = rgbe[1]; 1.403 + buffer[i + 2 * scanline_width] = rgbe[2]; 1.404 + buffer[i + 3 * scanline_width] = rgbe[3]; 1.405 + data += RGBE_DATA_SIZE; 1.406 + } 1.407 + /* write out each of the four channels separately run length encoded */ 1.408 + /* first red, then green, then blue, then exponent */ 1.409 + for(i = 0; i < 4; i++) { 1.410 + if((err = rgbe_write_bytes_rle(fp, &buffer[i * scanline_width], 1.411 + scanline_width)) != RGBE_RETURN_SUCCESS) { 1.412 + free(buffer); 1.413 + return err; 1.414 + } 1.415 + } 1.416 + } 1.417 + free(buffer); 1.418 + return RGBE_RETURN_SUCCESS; 1.419 +} 1.420 +#endif 1.421 + 1.422 +static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines) 1.423 +{ 1.424 + unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end; 1.425 + int i, count; 1.426 + unsigned char buf[2]; 1.427 + 1.428 + if((scanline_width < 8) || (scanline_width > 0x7fff)) 1.429 + /* run length encoding is not allowed so read flat */ 1.430 + return rgbe_read_pixels(io, data, scanline_width * num_scanlines); 1.431 + scanline_buffer = NULL; 1.432 + /* read in each successive scanline */ 1.433 + while(num_scanlines > 0) { 1.434 + if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) { 1.435 + free(scanline_buffer); 1.436 + return rgbe_error(rgbe_read_error, NULL); 1.437 + } 1.438 + if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) { 1.439 + /* this file is not run length encoded */ 1.440 + rgbe2float(&data[0], &data[1], &data[2], rgbe); 1.441 + data += RGBE_DATA_SIZE; 1.442 + free(scanline_buffer); 1.443 + return rgbe_read_pixels(io, data, scanline_width * num_scanlines - 1); 1.444 + } 1.445 + if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) { 1.446 + free(scanline_buffer); 1.447 + return rgbe_error(rgbe_format_error, "wrong scanline width"); 1.448 + } 1.449 + if(scanline_buffer == NULL) 1.450 + scanline_buffer = (unsigned char *) 1.451 + malloc(sizeof(unsigned char) * 4 * scanline_width); 1.452 + if(scanline_buffer == NULL) 1.453 + return rgbe_error(rgbe_memory_error, "unable to allocate buffer space"); 1.454 + 1.455 + ptr = &scanline_buffer[0]; 1.456 + /* read each of the four channels for the scanline into the buffer */ 1.457 + for(i = 0; i < 4; i++) { 1.458 + ptr_end = &scanline_buffer[(i + 1) * scanline_width]; 1.459 + while(ptr < ptr_end) { 1.460 + if(io->read(buf, sizeof(buf[0]) * 2, io->uptr) < 1) { 1.461 + free(scanline_buffer); 1.462 + return rgbe_error(rgbe_read_error, NULL); 1.463 + } 1.464 + if(buf[0] > 128) { 1.465 + /* a run of the same value */ 1.466 + count = buf[0] - 128; 1.467 + if((count == 0) || (count > ptr_end - ptr)) { 1.468 + free(scanline_buffer); 1.469 + return rgbe_error(rgbe_format_error, "bad scanline data"); 1.470 + } 1.471 + while(count-- > 0) 1.472 + *ptr++ = buf[1]; 1.473 + } else { 1.474 + /* a non-run */ 1.475 + count = buf[0]; 1.476 + if((count == 0) || (count > ptr_end - ptr)) { 1.477 + free(scanline_buffer); 1.478 + return rgbe_error(rgbe_format_error, "bad scanline data"); 1.479 + } 1.480 + *ptr++ = buf[1]; 1.481 + if(--count > 0) { 1.482 + if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) { 1.483 + free(scanline_buffer); 1.484 + return rgbe_error(rgbe_read_error, NULL); 1.485 + } 1.486 + ptr += count; 1.487 + } 1.488 + } 1.489 + } 1.490 + } 1.491 + /* now convert data from buffer into floats */ 1.492 + for(i = 0; i < scanline_width; i++) { 1.493 + rgbe[0] = scanline_buffer[i]; 1.494 + rgbe[1] = scanline_buffer[i + scanline_width]; 1.495 + rgbe[2] = scanline_buffer[i + 2 * scanline_width]; 1.496 + rgbe[3] = scanline_buffer[i + 3 * scanline_width]; 1.497 + rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe); 1.498 + data += RGBE_DATA_SIZE; 1.499 + } 1.500 + num_scanlines--; 1.501 + } 1.502 + free(scanline_buffer); 1.503 + return RGBE_RETURN_SUCCESS; 1.504 +}