|
@@ -1,413 +0,0 @@
|
|
|
-/* 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 <stdlib.h> //<malloc.h>
|
|
|
-#include <string.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;
|
|
|
-}
|
|
|
-
|