| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- /* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
- * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
- * IT IS STRICTLY USE AT YOUR OWN RISK. */
- #include "rgbe.h"
- #include <math.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- /* This file contains code to read and write four byte rgbe file format
- developed by Greg Ward. It handles the conversions between rgbe and
- pixels consisting of floats. The data is assumed to be an array of floats.
- By default there are three floats per pixel in the order red, green, blue.
- (RGBE_DATA_??? values control this.) Only the mimimal header reading and
- writing is implemented. Each routine does error checking and will return
- a status value as defined below. This code is intended as a skeleton so
- feel free to modify it to suit your needs.
- (Place notice here if you modified the code.)
- posted to http://www.graphics.cornell.edu/~bjw/
- written by Bruce Walter ([email protected]) 5/26/95
- based on code written by Greg Ward
- */
- #ifdef _CPLUSPLUS
- /* define if your compiler understands inline commands */
- #define INLINE inline
- #else
- #define INLINE
- #endif
- /* offsets to red, green, and blue components in a data (float) pixel */
- #define RGBE_DATA_RED 0
- #define RGBE_DATA_GREEN 1
- #define RGBE_DATA_BLUE 2
- /* number of floats per pixel */
- #define RGBE_DATA_SIZE 3
- enum rgbe_error_codes {
- rgbe_read_error,
- rgbe_write_error,
- rgbe_format_error,
- rgbe_memory_error,
- };
- /* default error routine. change this to change error handling */
- static int rgbe_error(int rgbe_error_code, char *msg)
- {
- switch (rgbe_error_code) {
- case rgbe_read_error:
- perror("RGBE read error");
- break;
- case rgbe_write_error:
- perror("RGBE write error");
- break;
- case rgbe_format_error:
- fprintf(stderr,"RGBE bad file format: %s\n",msg);
- break;
- default:
- case rgbe_memory_error:
- fprintf(stderr,"RGBE error: %s\n",msg);
- }
- return RGBE_RETURN_FAILURE;
- }
- /* standard conversion from float pixels to rgbe pixels */
- /* note: you can remove the "inline"s if your compiler complains about it */
- static INLINE void
- float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
- {
- float v;
- int e;
- v = red;
- if (green > v) v = green;
- if (blue > v) v = blue;
- if (v < 1e-32) {
- rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
- }
- else {
- v = frexp(v,&e) * 256.0/v;
- rgbe[0] = (unsigned char) (red * v);
- rgbe[1] = (unsigned char) (green * v);
- rgbe[2] = (unsigned char) (blue * v);
- rgbe[3] = (unsigned char) (e + 128);
- }
- }
- /* standard conversion from rgbe to float pixels */
- /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
- /* in the range [0,1] to map back into the range [0,1]. */
- static INLINE void
- rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
- {
- float f;
- if (rgbe[3]) { /*nonzero pixel*/
- f = ldexp(1.0,rgbe[3]-(int)(128+8));
- *red = rgbe[0] * f;
- *green = rgbe[1] * f;
- *blue = rgbe[2] * f;
- }
- else
- *red = *green = *blue = 0.0;
- }
- /* default minimal header. modify if you want more information in header */
- int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info)
- {
- char *programtype = "RGBE";
- if (info && (info->valid & RGBE_VALID_PROGRAMTYPE))
- programtype = info->programtype;
- if (fprintf(fp,"#?%s\n",programtype) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- /* The #? is to identify file type, the programtype is optional. */
- if (info && (info->valid & RGBE_VALID_GAMMA)) {
- if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- }
- if (info && (info->valid & RGBE_VALID_EXPOSURE)) {
- if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- }
- if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0)
- return rgbe_error(rgbe_write_error,NULL);
- if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- return RGBE_RETURN_SUCCESS;
- }
- /* minimal header reading. modify if you want to parse more information */
- int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info)
- {
- char buf[128];
- int found_format;
- float tempf;
- int i;
- found_format = 0;
- if (info) {
- info->valid = 0;
- info->programtype[0] = 0;
- info->gamma = info->exposure = 1.0;
- }
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL)
- return rgbe_error(rgbe_read_error,NULL);
- if ((buf[0] != '#')||(buf[1] != '?')) {
- /* if you want to require the magic token then uncomment the next line */
- /*return rgbe_error(rgbe_format_error,"bad initial token"); */
- }
- else if (info) {
- info->valid |= RGBE_VALID_PROGRAMTYPE;
- for(i=0;i<sizeof(info->programtype)-1;i++) {
- if ((buf[i+2] == 0) || isspace(buf[i+2]))
- break;
- info->programtype[i] = buf[i+2];
- }
- info->programtype[i] = 0;
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- }
- for(;;) {
- if ((buf[0] == 0)||(buf[0] == '\n'))
- return rgbe_error(rgbe_format_error,"no FORMAT specifier found");
- else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0)
- break; /* format found so break out of loop */
- else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) {
- info->gamma = tempf;
- info->valid |= RGBE_VALID_GAMMA;
- }
- else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) {
- info->exposure = tempf;
- info->valid |= RGBE_VALID_EXPOSURE;
- }
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- }
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- if (strcmp(buf,"\n") != 0)
- return rgbe_error(rgbe_format_error,
- "missing blank line after FORMAT specifier");
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- if (sscanf(buf,"-Y %d +X %d",height,width) < 2)
- return rgbe_error(rgbe_format_error,"missing image size specifier");
- return RGBE_RETURN_SUCCESS;
- }
- /* simple write routine that does not use run length encoding */
- /* These routines can be made faster by allocating a larger buffer and
- fread-ing and fwrite-ing the data in larger chunks */
- int RGBE_WritePixels(FILE *fp, float *data, int numpixels)
- {
- unsigned char rgbe[4];
- while (numpixels-- > 0) {
- float2rgbe(rgbe,data[RGBE_DATA_RED],
- data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
- data += RGBE_DATA_SIZE;
- if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- }
- return RGBE_RETURN_SUCCESS;
- }
- /* simple read routine. will not correctly handle run length encoding */
- int RGBE_ReadPixels(FILE *fp, float *data, int numpixels)
- {
- unsigned char rgbe[4];
- while(numpixels-- > 0) {
- if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
- return rgbe_error(rgbe_read_error,NULL);
- rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
- &data[RGBE_DATA_BLUE],rgbe);
- data += RGBE_DATA_SIZE;
- }
- return RGBE_RETURN_SUCCESS;
- }
- /* The code below is only needed for the run-length encoded files. */
- /* Run length encoding adds considerable complexity but does */
- /* save some space. For each scanline, each channel (r,g,b,e) is */
- /* encoded separately for better compression. */
- static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes)
- {
- #define MINRUNLENGTH 4
- int cur, beg_run, run_count, old_run_count, nonrun_count;
- unsigned char buf[2];
- cur = 0;
- while(cur < numbytes) {
- beg_run = cur;
- /* find next run of length at least 4 if one exists */
- run_count = old_run_count = 0;
- while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
- beg_run += run_count;
- old_run_count = run_count;
- run_count = 1;
- while( (beg_run + run_count < numbytes) && (run_count < 127)
- && (data[beg_run] == data[beg_run + run_count]))
- run_count++;
- }
- /* if data before next big run is a short run then write it as such */
- if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
- buf[0] = 128 + old_run_count; /*write short run*/
- buf[1] = data[cur];
- if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- cur = beg_run;
- }
- /* write out bytes until we reach the start of the next run */
- while(cur < beg_run) {
- nonrun_count = beg_run - cur;
- if (nonrun_count > 128)
- nonrun_count = 128;
- buf[0] = nonrun_count;
- if (fwrite(buf,sizeof(buf[0]),1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- cur += nonrun_count;
- }
- /* write out next run if one was found */
- if (run_count >= MINRUNLENGTH) {
- buf[0] = 128 + run_count;
- buf[1] = data[beg_run];
- if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- cur += run_count;
- }
- }
- return RGBE_RETURN_SUCCESS;
- #undef MINRUNLENGTH
- }
- int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
- int num_scanlines)
- {
- unsigned char rgbe[4];
- unsigned char *buffer;
- int i, err;
- if ((scanline_width < 8)||(scanline_width > 0x7fff))
- /* run length encoding is not allowed so write flat*/
- return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
- buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width);
- if (buffer == NULL)
- /* no buffer space so write flat */
- return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
- while(num_scanlines-- > 0) {
- rgbe[0] = 2;
- rgbe[1] = 2;
- rgbe[2] = scanline_width >> 8;
- rgbe[3] = scanline_width & 0xFF;
- if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
- free(buffer);
- return rgbe_error(rgbe_write_error,NULL);
- }
- for(i=0;i<scanline_width;i++) {
- float2rgbe(rgbe,data[RGBE_DATA_RED],
- data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
- buffer[i] = rgbe[0];
- buffer[i+scanline_width] = rgbe[1];
- buffer[i+2*scanline_width] = rgbe[2];
- buffer[i+3*scanline_width] = rgbe[3];
- data += RGBE_DATA_SIZE;
- }
- /* write out each of the four channels separately run length encoded */
- /* first red, then green, then blue, then exponent */
- for(i=0;i<4;i++) {
- if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width],
- scanline_width)) != RGBE_RETURN_SUCCESS) {
- free(buffer);
- return err;
- }
- }
- }
- free(buffer);
- return RGBE_RETURN_SUCCESS;
- }
-
- int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
- int num_scanlines)
- {
- unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
- int i, count;
- unsigned char buf[2];
- if ((scanline_width < 8)||(scanline_width > 0x7fff))
- /* run length encoding is not allowed so read flat*/
- return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
- scanline_buffer = NULL;
- /* read in each successive scanline */
- while(num_scanlines > 0) {
- if (fread(rgbe,sizeof(rgbe),1,fp) < 1) {
- free(scanline_buffer);
- return rgbe_error(rgbe_read_error,NULL);
- }
- if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
- /* this file is not run length encoded */
- rgbe2float(&data[0],&data[1],&data[2],rgbe);
- data += RGBE_DATA_SIZE;
- free(scanline_buffer);
- return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
- }
- if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
- free(scanline_buffer);
- return rgbe_error(rgbe_format_error,"wrong scanline width");
- }
- if (scanline_buffer == NULL)
- scanline_buffer = (unsigned char *)
- malloc(sizeof(unsigned char)*4*scanline_width);
- if (scanline_buffer == NULL)
- return rgbe_error(rgbe_memory_error,"unable to allocate buffer space");
-
- ptr = &scanline_buffer[0];
- /* read each of the four channels for the scanline into the buffer */
- for(i=0;i<4;i++) {
- ptr_end = &scanline_buffer[(i+1)*scanline_width];
- while(ptr < ptr_end) {
- if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) {
- free(scanline_buffer);
- return rgbe_error(rgbe_read_error,NULL);
- }
- if (buf[0] > 128) {
- /* a run of the same value */
- count = buf[0]-128;
- if ((count == 0)||(count > ptr_end - ptr)) {
- free(scanline_buffer);
- return rgbe_error(rgbe_format_error,"bad scanline data");
- }
- while(count-- > 0)
- *ptr++ = buf[1];
- }
- else {
- /* a non-run */
- count = buf[0];
- if ((count == 0)||(count > ptr_end - ptr)) {
- free(scanline_buffer);
- return rgbe_error(rgbe_format_error,"bad scanline data");
- }
- *ptr++ = buf[1];
- if (--count > 0) {
- if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) {
- free(scanline_buffer);
- return rgbe_error(rgbe_read_error,NULL);
- }
- ptr += count;
- }
- }
- }
- }
- /* now convert data from buffer into floats */
- for(i=0;i<scanline_width;i++) {
- rgbe[0] = scanline_buffer[i];
- rgbe[1] = scanline_buffer[i+scanline_width];
- rgbe[2] = scanline_buffer[i+2*scanline_width];
- rgbe[3] = scanline_buffer[i+3*scanline_width];
- rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
- &data[RGBE_DATA_BLUE],rgbe);
- data += RGBE_DATA_SIZE;
- }
- num_scanlines--;
- }
- free(scanline_buffer);
- return RGBE_RETURN_SUCCESS;
- }
|