diff options
Diffstat (limited to 'DevIL/src-IL/src/il_hdr.cpp')
-rw-r--r-- | DevIL/src-IL/src/il_hdr.cpp | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/DevIL/src-IL/src/il_hdr.cpp b/DevIL/src-IL/src/il_hdr.cpp new file mode 100644 index 00000000..55b9bd16 --- /dev/null +++ b/DevIL/src-IL/src/il_hdr.cpp @@ -0,0 +1,659 @@ +//----------------------------------------------------------------------------- +// +// ImageLib Sources +// Copyright (C) 2000-2008 by Denton Woods (this file by thakis / Denton) +// Last modified: 02/09/2009 +// +// Filename: src-IL/src/il_hdr.c +// +// Description: Reads/writes a RADIANCE High Dynamic Range Image +// +//----------------------------------------------------------------------------- + + +#include "il_internal.h" +#ifndef IL_NO_HDR +#include "il_hdr.h" +#include "il_endian.h" + + +//! Checks if the file specified in FileName is a valid .hdr file. +ILboolean ilIsValidHdr(ILconst_string FileName) +{ + ILHANDLE HdrFile; + ILboolean bHdr = IL_FALSE; + + if (!iCheckExtension(FileName, IL_TEXT("hdr"))) { + ilSetError(IL_INVALID_EXTENSION); + return bHdr; + } + + HdrFile = iopenr(FileName); + if (HdrFile == NULL) { + ilSetError(IL_COULD_NOT_OPEN_FILE); + return bHdr; + } + + bHdr = ilIsValidHdrF(HdrFile); + icloser(HdrFile); + + return bHdr; +} + + +//! Checks if the ILHANDLE contains a valid .hdr file at the current position. +ILboolean ilIsValidHdrF(ILHANDLE File) +{ + ILuint FirstPos; + ILboolean bRet; + + iSetInputFile(File); + FirstPos = itell(); + bRet = iIsValidHdr(); + iseek(FirstPos, IL_SEEK_SET); + + return bRet; +} + + +//! Checks if Lump is a valid .hdr lump. +ILboolean ilIsValidHdrL(const void *Lump, ILuint Size) +{ + iSetInputLump(Lump, Size); + return iIsValidHdr(); +} + + +// Internal function used to get the .hdr header from the current file. +ILboolean iGetHdrHead(HDRHEADER *Header) +{ + ILboolean done = IL_FALSE; + char a, b; + char x[3], y[3]; //changed 20050217: added space for the '\0' char + char buff[81]; // 01-19-2009: Added space for the '\0'. + ILuint count = 0; + + iread(Header->Signature, 1, 10); + + //skip lines until an empty line is found. + //this marks the end of header information, + //the next line contains the image's dimension. + + //TODO: read header contents into variables + //(EXPOSURE, orientation, xyz correction, ...) + + if (iread(&a, 1, 1) != 1) + return IL_FALSE; + + while (!done) { + if (iread(&b, 1, 1) != 1) + return IL_FALSE; + if (b == '\n' && a == '\n') + done = IL_TRUE; + else + a = b; + } + + //read dimensions (note that this assumes a somewhat valid image) + if (iread(&a, 1, 1) != 1) + return IL_FALSE; + while (a != '\n') { + if (count >= 80) { // Line shouldn't be this long at all. + ilSetError(IL_INVALID_FILE_HEADER); + return IL_FALSE; + } + buff[count] = a; + if (iread(&a, 1, 1) != 1) + return IL_FALSE; + ++count; + } + buff[count] = '\0'; + + //note that this is not the 100% correct way to load hdr images: + //in a perfect world we would check if there's a +/- X/Y in front + //of width and heigth and mirror + rotate the image after decoding + //according to that. But HDRShop doesn't do that either (and that's + //my reference program :) ) and it's just a rotate and a mirror, + //nothing that really changes the appearance of the loaded image... + //(The code as it is now assumes that y contains "-Y" and x contains + //"+X" after the following line) + + // The 2 has to be in the %s format specifier to prevent buffer overruns. + sscanf(buff, "%2s %d %2s %d", y, &Header->Height, x, &Header->Width); + + return IL_TRUE; +} + + +// Internal function to get the header and check it. +ILboolean iIsValidHdr() +{ + char Head[10]; + ILint Read; + + Read = iread(Head, 1, 10); + iseek(-Read, IL_SEEK_CUR); // Go ahead and restore to previous state + if (Read != 10) + return IL_FALSE; + + return + strnicmp(Head, "#?RADIANCE", 10) == 0 + || strnicmp(Head, "#?RGBE", 6) == 0; +} + + +// Internal function used to check if the HEADER is a valid .hdr header. +ILboolean iCheckHdr(HDRHEADER *Header) +{ + return + strnicmp(Header->Signature, "#?RADIANCE", 10) == 0 + || strnicmp(Header->Signature, "#?RGBE", 6) == 0; +} + + +//! Reads a .hdr file +ILboolean ilLoadHdr(ILconst_string FileName) +{ + ILHANDLE HdrFile; + ILboolean bHdr = IL_FALSE; + + HdrFile = iopenr(FileName); + if (HdrFile == NULL) { + ilSetError(IL_COULD_NOT_OPEN_FILE); + return bHdr; + } + + bHdr = ilLoadHdrF(HdrFile); + icloser(HdrFile); + + return bHdr; +} + + +//! Reads an already-opened .hdr file +ILboolean ilLoadHdrF(ILHANDLE File) +{ + ILuint FirstPos; + ILboolean bRet; + + iSetInputFile(File); + FirstPos = itell(); + bRet = iLoadHdrInternal(); + iseek(FirstPos, IL_SEEK_SET); + + return bRet; +} + + +//! Reads from a memory "lump" that contains a .hdr +ILboolean ilLoadHdrL(const void *Lump, ILuint Size) +{ + iSetInputLump(Lump, Size); + return iLoadHdrInternal(); +} + + +// Internal function used to load the .hdr. +ILboolean iLoadHdrInternal() +{ + HDRHEADER Header; + ILfloat *data; + ILubyte *scanline; + ILuint i, j, e, r, g, b; + + if (iCurImage == NULL) { + ilSetError(IL_ILLEGAL_OPERATION); + return IL_FALSE; + } + + if (!iGetHdrHead(&Header)) { + ilSetError(IL_INVALID_FILE_HEADER); + return IL_FALSE; + } + if (!iCheckHdr(&Header)) { + //iseek(-(ILint)sizeof(HDRHEAD), IL_SEEK_CUR); + ilSetError(IL_INVALID_FILE_HEADER); + return IL_FALSE; + } + + // Update the current image with the new dimensions + if (!ilTexImage(Header.Width, Header.Height, 1, 3, IL_RGB, IL_FLOAT, NULL)) { + return IL_FALSE; + } + iCurImage->Origin = IL_ORIGIN_UPPER_LEFT; + + //read image data + if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) + iPreCache(iCurImage->Width / 8 * iCurImage->Height); + + data = (ILfloat*)iCurImage->Data; + scanline = (ILubyte*)ialloc(Header.Width*4); + for (i = 0; i < Header.Height; ++i) { + ReadScanline(scanline, Header.Width); + + //convert hdrs internal format to floats + for (j = 0; j < 4*Header.Width; j += 4) { + ILuint *ee; + ILfloat t, *ff; + e = scanline[j + 3]; + r = scanline[j + 0]; + g = scanline[j + 1]; + b = scanline[j + 2]; + + //t = (float)pow(2.f, ((ILint)e) - 128); + if (e != 0) + e = (e - 1) << 23; + + // All this just to avoid stric-aliasing warnings... + // was: t = *(ILfloat*)&e + ee = &e; + ff = (ILfloat*)ee; + t = *ff; + + data[0] = (r/255.0f)*t; + data[1] = (g/255.0f)*t; + data[2] = (b/255.0f)*t; + data += 3; + } + } + iUnCache(); + ifree(scanline); + + return ilFixImage(); +} + +void ReadScanline(ILubyte *scanline, ILuint w) { + ILubyte *runner; + ILuint r, g, b, e, read, shift; + + r = igetc(); + g = igetc(); + b = igetc(); + e = igetc(); + + //check if the scanline is in the new format + //if so, e, r, g, g are stored separated and are + //rle-compressed independently. + if (r == 2 && g == 2) { + ILuint length = (b << 8) | e; + ILuint j, t, k; + if (length > w) + length = w; //fix broken files + for (k = 0; k < 4; ++k) { + runner = scanline + k; + j = 0; + while (j < length) { + t = igetc(); + if (t > 128) { //Run? + ILubyte val = igetc(); + t &= 127; + //copy current byte + while (t > 0 && j < length) { + *runner = val; + runner += 4; + --t; + ++j; + } + } + else { //No Run. + //read new bytes + while (t > 0 && j < length) { + *runner = igetc(); + runner += 4; + --t; + ++j; + } + } + } + } + return; //done decoding a scanline in separated format + } + + //if we come here, we are dealing with old-style scanlines + shift = 0; + read = 0; + runner = scanline; + while (read < w) { + if (read != 0) { + r = igetc(); + g = igetc(); + b = igetc(); + e = igetc(); + } + + //if all three mantissas are 1, then this is a rle + //count dword + if (r == 1 && g == 1 && b == 1) { + ILuint length = e; + ILuint j; + for (j = length << shift; j > 0 && read < w; --j) { + memcpy(runner, runner - 4, 4); + runner += 4; + ++read; + } + //if more than one rle count dword is read + //consecutively, they are higher order bytes + //of the first read value. shift keeps track of + //that. + shift += 8; + } + else { + runner[0] = r; + runner[1] = g; + runner[2] = b; + runner[3] = e; + + shift = 0; + runner += 4; + ++read; + } + } +} + + + +//! Writes a Hdr file +ILboolean ilSaveHdr(const ILstring FileName) +{ + ILHANDLE HdrFile; + ILuint HdrSize; + + if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) { + if (iFileExists(FileName)) { + ilSetError(IL_FILE_ALREADY_EXISTS); + return IL_FALSE; + } + } + + HdrFile = iopenw(FileName); + if (HdrFile == NULL) { + ilSetError(IL_COULD_NOT_OPEN_FILE); + return IL_FALSE; + } + + HdrSize = ilSaveHdrF(HdrFile); + iclosew(HdrFile); + + if (HdrSize == 0) + return IL_FALSE; + return IL_TRUE; +} + + +//! Writes a Hdr to an already-opened file +ILuint ilSaveHdrF(ILHANDLE File) +{ + ILuint Pos; + iSetOutputFile(File); + Pos = itellw(); + if (iSaveHdrInternal() == IL_FALSE) + return 0; // Error occurred + return itellw() - Pos; // Return the number of bytes written. +} + + +//! Writes a Hdr to a memory "lump" +ILuint ilSaveHdrL(void *Lump, ILuint Size) +{ + ILuint Pos; + iSetOutputLump(Lump, Size); + Pos = itellw(); + if (iSaveHdrInternal() == IL_FALSE) + return 0; // Error occurred + return itellw() - Pos; // Return the number of bytes written. +} + + +// +// Much of the saving code is based on the code by Bruce Walter, +// available at http://www.graphics.cornell.edu/online/formats/rgbe/. +// +// The actual source code file is +// http://www.graphics.cornell.edu/online/formats/rgbe/rgbe.c +// + + +/* standard conversion from float pixels to rgbe pixels */ +/* note: you can remove the "inline"s if your compiler complains about it */ +//static INLINE void +static 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 = (float)(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); + } +} + + +typedef struct { + ILuint valid; /* indicate which fields are valid */ + ILbyte programtype[16]; /* listed at beginning of file to identify it + * after "#?". defaults to "RGBE" */ + ILfloat gamma; /* image has already been gamma corrected with + * given gamma. defaults to 1.0 (no correction) */ + ILfloat exposure; /* a value of 1.0 in an image corresponds to + * <exposure> watts/steradian/m^2. + * defaults to 1.0 */ +} rgbe_header_info; + +/* flags indicating which fields in an rgbe_header_info are valid */ +#define RGBE_VALID_PROGRAMTYPE 0x01 +#define RGBE_VALID_GAMMA 0x02 +#define RGBE_VALID_EXPOSURE 0x04 + +/* 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 + + +/* default minimal header. modify if you want more information in header */ +ILboolean RGBE_WriteHeader(ILuint width, ILuint height, rgbe_header_info *info) +{ + char *programtype = "RGBE"; + + if (info && (info->valid & RGBE_VALID_PROGRAMTYPE)) + programtype = info->programtype; + if (ilprintf("#?%s\n",programtype) < 0) + return IL_FALSE; + /* The #? is to identify file type, the programtype is optional. */ + if (info && (info->valid & RGBE_VALID_GAMMA)) { + if (ilprintf("GAMMA=%g\n",info->gamma) < 0) + return IL_FALSE; + } + if (info && (info->valid & RGBE_VALID_EXPOSURE)) { + if (ilprintf("EXPOSURE=%g\n",info->exposure) < 0) + return IL_FALSE; + } + if (ilprintf("FORMAT=32-bit_rle_rgbe\n\n") < 0) + return IL_FALSE; + if (ilprintf("-Y %d +X %d\n", height, width) < 0) + return IL_FALSE; + return IL_TRUE; +} + + +/* 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 iwrite-ing the data in larger chunks */ +int RGBE_WritePixels(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 (iwrite(rgbe, sizeof(rgbe), 1) < 1) + return IL_FALSE; + } + return IL_TRUE; +} + + +/* 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. */ + +ILboolean RGBE_WriteBytes_RLE(ILubyte *data, ILuint numbytes) +{ +#define MINRUNLENGTH 4 + ILuint cur, beg_run, run_count, old_run_count, nonrun_count; + ILubyte 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; + // 01-25-2009: Moved test for beg_run + run_count first so that it is + // tested first. This keeps it from going out of bounds by 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 (iwrite(buf,sizeof(buf[0])*2,1) < 1) + return IL_FALSE; + 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 (iwrite(buf,sizeof(buf[0]),1) < 1) + return IL_FALSE; + if (iwrite(&data[cur],sizeof(data[0])*nonrun_count,1) < 1) + return IL_FALSE; + 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 (iwrite(buf,sizeof(buf[0])*2,1) < 1) + return IL_FALSE; + cur += run_count; + } + } + return IL_TRUE; +#undef MINRUNLENGTH +} + + +// Internal function used to save the Hdr. +ILboolean iSaveHdrInternal() +{ + ILimage *TempImage; + rgbe_header_info stHeader; + unsigned char rgbe[4]; + ILubyte *buffer; + ILfloat *data; + ILuint i; + ILboolean bRet; + + if (iCurImage == NULL) { + ilSetError(IL_ILLEGAL_OPERATION); + return IL_FALSE; + } + + stHeader.exposure = 0; + stHeader.gamma = 0; + stHeader.programtype[0] = 0; + stHeader.valid = 0; + + if (iCurImage->Format != IL_UNSIGNED_BYTE) { + TempImage = iConvertImage(iCurImage, IL_RGB, IL_FLOAT); + if (TempImage == NULL) + return IL_FALSE; + } + else + TempImage = iCurImage; + + if (!RGBE_WriteHeader(TempImage->Width, TempImage->Height, &stHeader)) + return IL_FALSE; + + if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT) + iFlipBuffer(TempImage->Data, TempImage->Depth, TempImage->Bps, TempImage->Height); + data = (ILfloat*)TempImage->Data; + + if ((TempImage->Width < 8)||(TempImage->Width > 0x7fff)) { + /* run length encoding is not allowed so write flat*/ + bRet = RGBE_WritePixels(data,TempImage->Width*TempImage->Height); + if (iCurImage != TempImage) + ilCloseImage(TempImage); + return bRet; + } + buffer = (ILubyte*)ialloc(sizeof(ILubyte)*4*TempImage->Width); + if (buffer == NULL) { + /* no buffer space so write flat */ + bRet = RGBE_WritePixels(data,TempImage->Width*TempImage->Height); + if (iCurImage != TempImage) + ilCloseImage(TempImage); + return bRet; + } + + while(TempImage->Height-- > 0) { + rgbe[0] = 2; + rgbe[1] = 2; + rgbe[2] = TempImage->Width >> 8; + rgbe[3] = TempImage->Width & 0xFF; + if (iwrite(rgbe, sizeof(rgbe), 1) < 1) { + free(buffer); + if (iCurImage != TempImage) + ilCloseImage(TempImage); + return IL_FALSE; + } + + for(i=0;i<TempImage->Width;i++) { + float2rgbe(rgbe,data[RGBE_DATA_RED],data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]); + buffer[i] = rgbe[0]; + buffer[i+TempImage->Width] = rgbe[1]; + buffer[i+2*TempImage->Width] = rgbe[2]; + buffer[i+3*TempImage->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 (RGBE_WriteBytes_RLE(&buffer[i*TempImage->Width],TempImage->Width) != IL_TRUE) { + ifree(buffer); + if (iCurImage != TempImage) + ilCloseImage(TempImage); + return IL_FALSE; + } + } + } + ifree(buffer); + + if (iCurImage != TempImage) + ilCloseImage(TempImage); + return IL_TRUE; +} + + +#endif//IL_NO_HDR |