dbf-halloween2015

annotate libs/imago/file_rgbe.c @ 3:c37fe5d8a4ed

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