Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/FreeImage/PluginHDR.cpp view on Meta::CPAN
// ==========================================================
// Plugin Interface
// ==========================================================
static int s_format_id;
// ==========================================================
// RGBE library
// ==========================================================
// ----------------------------------------------------------
// maximum size of a line in the header
#define HDR_MAXLINE 256
// flags indicating which fields in an rgbeHeaderInfo are valid
#define RGBE_VALID_PROGRAMTYPE 0x01
#define RGBE_VALID_COMMENT 0x02
#define RGBE_VALID_GAMMA 0x04
#define RGBE_VALID_EXPOSURE 0x08
// 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
// ----------------------------------------------------------
#ifdef _WIN32
#pragma pack(push, 1)
#else
#pragma pack(1)
#endif
typedef struct tagHeaderInfo {
int valid; // indicate which fields are valid
char programtype[16]; // listed at beginning of file to identify it after "#?". defaults to "RGBE"
char comment[HDR_MAXLINE]; // comment beginning with "# "
float gamma; // image has already been gamma corrected with given gamma. defaults to 1.0 (no correction)
float exposure; // a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0
} rgbeHeaderInfo;
#ifdef _WIN32
#pragma pack(pop)
#else
#pragma pack()
#endif
typedef enum {
rgbe_read_error,
rgbe_write_error,
rgbe_format_error,
rgbe_memory_error
} rgbe_error_code;
// ----------------------------------------------------------
// Prototypes
// ----------------------------------------------------------
static BOOL rgbe_Error(rgbe_error_code error_code, const char *msg);
static BOOL rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length);
static inline void rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf);
static inline void rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]);
static BOOL rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info);
static BOOL rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info);
static BOOL rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
static BOOL rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
static BOOL rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines);
static BOOL rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes);
static BOOL rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines);
static BOOL rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
static BOOL rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
// ----------------------------------------------------------
/**
Default error routine. change this to change error handling
*/
static BOOL
rgbe_Error(rgbe_error_code error_code, const char *msg) {
switch (error_code) {
case rgbe_read_error:
FreeImage_OutputMessageProc(s_format_id, "RGBE read error");
break;
case rgbe_write_error:
FreeImage_OutputMessageProc(s_format_id, "RGBE write error");
break;
case rgbe_format_error:
FreeImage_OutputMessageProc(s_format_id, "RGBE bad file format: %s\n", msg);
break;
default:
case rgbe_memory_error:
FreeImage_OutputMessageProc(s_format_id, "RGBE error: %s\n",msg);
}
return FALSE;
}
/**
Get a line from a ASCII io stream
*/
static BOOL
rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
int i;
memset(buffer, 0, length);
for(i = 0; i < length; i++) {
if(!io->read_proc(&buffer[i], 1, 1, handle))
return FALSE;
if(buffer[i] == 0x0A)
break;
}
return (i < length) ? TRUE : FALSE;
}
/**
Standard conversion from float pixels to rgbe pixels.
Note: you can remove the "inline"s if your compiler complains about it
*/
static inline void
rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf) {
float v;
int e;
v = rgbf->red;
if (rgbf->green > v) v = rgbf->green;
if (rgbf->blue > v) v = rgbf->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] = (BYTE) (rgbf->red * v);
rgbe[1] = (BYTE) (rgbf->green * v);
rgbe[2] = (BYTE) (rgbf->blue * v);
rgbe[3] = (BYTE) (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
rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]) {
if (rgbe[3]) { // nonzero pixel
float f = (float)(ldexp(1.0, rgbe[3] - (int)(128+8)));
rgbf->red = rgbe[0] * f;
rgbf->green = rgbe[1] * f;
rgbf->blue = rgbe[2] * f;
}
else {
rgbf->red = rgbf->green = rgbf->blue = 0;
}
}
/**
Minimal header reading. Modify if you want to parse more information
*/
static BOOL
rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info) {
char buf[HDR_MAXLINE];
float tempf;
int i;
BOOL bFormatFound = FALSE;
BOOL bHeaderFound = FALSE;
header_info->valid = 0;
header_info->programtype[0] = 0;
header_info->gamma = 1.0;
header_info->exposure = 1.0;
src/Source/FreeImage/PluginHDR.cpp view on Meta::CPAN
}
}
return TRUE;
}
/**
default minimal header. modify if you want more information in header
*/
static BOOL
rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info) {
char buffer[HDR_MAXLINE];
const char *programtype = "RADIANCE";
if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) {
programtype = info->programtype;
}
// The #? is to identify file type, the programtype is optional
sprintf(buffer, "#?%s\n", programtype);
if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
sprintf(buffer, "%s\n", info->comment);
if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
sprintf(buffer, "FORMAT=32-bit_rle_rgbe\n");
if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
if(info && (info->valid & RGBE_VALID_GAMMA)) {
sprintf(buffer, "GAMMA=%g\n", info->gamma);
if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
}
if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
sprintf(buffer,"EXPOSURE=%g\n", info->exposure);
if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
}
sprintf(buffer, "\n-Y %d +X %d\n", height, width);
if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
return TRUE;
}
static BOOL
rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
return TRUE;
}
static BOOL
rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
header_info->gamma = 1;
header_info->valid |= RGBE_VALID_GAMMA;
header_info->exposure = 0;
header_info->valid |= RGBE_VALID_EXPOSURE;
return TRUE;
}
/**
Simple read routine. Will not correctly handle run length encoding
*/
static BOOL
rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
BYTE rgbe[4];
for(unsigned x = 0; x < numpixels; x++) {
if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
return rgbe_Error(rgbe_read_error, NULL);
}
rgbe_RGBEToFloat(&data[x], rgbe);
}
return 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 fwrite-ing the data in larger chunks.
*/
static BOOL
rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
BYTE rgbe[4];
for(unsigned x = 0; x < numpixels; x++) {
rgbe_FloatToRGBE(rgbe, &data[x]);
if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1)
return rgbe_Error(rgbe_write_error, NULL);
}
return TRUE;
}
static BOOL
rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines) {
BYTE rgbe[4], *scanline_buffer, *ptr, *ptr_end;
int i, count;
BYTE buf[2];
if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
// run length encoding is not allowed so read flat
return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines);
}
scanline_buffer = NULL;
// read in each successive scanline
while(num_scanlines > 0) {
if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 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
rgbe_RGBEToFloat(data, rgbe);
data ++;
free(scanline_buffer);
return rgbe_ReadPixels(io, handle, 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 = (BYTE*)malloc(sizeof(BYTE) * 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(io->read_proc(buf, 1, 2 * sizeof(BYTE), handle) < 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(io->read_proc(ptr, 1, sizeof(BYTE) * count, handle) < 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];
rgbe_RGBEToFloat(data, rgbe);
data ++;
}
num_scanlines--;
}
free(scanline_buffer);
return 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.
@return Returns TRUE if successful, returns FALSE otherwise
*/
static BOOL
rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes) {
static const int MINRUNLENGTH = 4;
int cur, beg_run, run_count, old_run_count, nonrun_count;
BYTE 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] = (BYTE)(128 + old_run_count); // write short run
buf[1] = data[cur];
if(io->write_proc(buf, 2 * sizeof(BYTE), 1, handle) < 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] = (BYTE)nonrun_count;
if(io->write_proc(buf, sizeof(buf[0]), 1, handle) < 1)
return rgbe_Error(rgbe_write_error,NULL);
if(io->write_proc(&data[cur], sizeof(data[0]) * nonrun_count, 1, handle) < 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] = (BYTE)(128 + run_count);
buf[1] = data[beg_run];
if(io->write_proc(buf, sizeof(buf[0]) * 2, 1, handle) < 1)
return rgbe_Error(rgbe_write_error,NULL);
cur += run_count;
}
}
return TRUE;
}
static BOOL
rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines) {
BYTE rgbe[4];
BYTE *buffer;
if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
// run length encoding is not allowed so write flat
return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
}
buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
if (buffer == NULL) {
// no buffer space so write flat
return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
}
while(num_scanlines-- > 0) {
rgbe[0] = (BYTE)2;
rgbe[1] = (BYTE)2;
rgbe[2] = (BYTE)(scanline_width >> 8);
rgbe[3] = (BYTE)(scanline_width & 0xFF);
if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1) {
free(buffer);
return rgbe_Error(rgbe_write_error, NULL);
}
for(unsigned x = 0; x < scanline_width; x++) {
rgbe_FloatToRGBE(rgbe, data);
buffer[x] = rgbe[0];
buffer[x+scanline_width] = rgbe[1];
buffer[x+2*scanline_width] = rgbe[2];
buffer[x+3*scanline_width] = rgbe[3];
data ++;
}
// write out each of the four channels separately run length encoded
// first red, then green, then blue, then exponent
for(int i = 0; i < 4; i++) {
BOOL bOK = rgbe_WriteBytes_RLE(io, handle, &buffer[i*scanline_width], scanline_width);
if(!bOK) {
free(buffer);
return bOK;
}
}
}
free(buffer);
return TRUE;
}
// ----------------------------------------------------------
// ==========================================================
// Plugin Implementation
// ==========================================================
static const char * DLL_CALLCONV
Format() {
return "HDR";
}
static const char * DLL_CALLCONV
Description() {
return "High Dynamic Range Image";
}
static const char * DLL_CALLCONV
Extension() {
return "hdr";
}
static const char * DLL_CALLCONV
RegExpr() {
return NULL;
}
static const char * DLL_CALLCONV
MimeType() {
return "image/vnd.radiance";
}
static BOOL DLL_CALLCONV
Validate(FreeImageIO *io, fi_handle handle) {
BYTE hdr_signature[] = { '#', '?' };
BYTE signature[] = { 0, 0 };
io->read_proc(signature, 1, 2, handle);
return (memcmp(hdr_signature, signature, 2) == 0);
}
static BOOL DLL_CALLCONV
SupportsExportDepth(int depth) {
( run in 1.051 second using v1.01-cache-2.11-cpan-119454b85a5 )