diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/PluginPFM.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/PluginPFM.cpp | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp b/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp new file mode 100644 index 0000000000..8ccbd48ba5 --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp @@ -0,0 +1,402 @@ +// ========================================================== +// PFM Loader and Writer +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ========================================================== +// Internal functions +// ========================================================== + +/** maximum size of a line in the header */ +#define PFM_MAXLINE 256 + +/** Big endian / Little endian float conversion */ +#define REVERSEBYTES(source, dest) \ +{ \ + char *j = (char *) source; \ + char *dj = (char *) dest; \ + dj[0] = j[3]; \ + dj[1] = j[2]; \ + dj[2] = j[1]; \ + dj[3] = j[0]; \ +} + +/** +Get a line from a ASCII io stream +*/ +static BOOL +pfm_get_line(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; +} + +/** +Get an integer value from the actual position pointed by handle +*/ +static int +pfm_get_int(FreeImageIO *io, fi_handle handle) { + char c = 0; + BOOL firstchar; + + // skip forward to start of next number + + if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + + while (1) { + // eat comments + + if (c == '#') { + // if we're at a comment, read to end of line + + firstchar = TRUE; + + while (1) { + if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + + if (firstchar && c == ' ') { + // loop off 1 sp after # + + firstchar = FALSE; + } else if (c == '\n') { + break; + } + } + } + + if (c >= '0' && c <='9') { + // we've found what we were looking for + + break; + } + + if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + } + + // we're at the start of a number, continue until we hit a non-number + + int i = 0; + + while (1) { + i = (i * 10) + (c - '0'); + + if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + + if (c < '0' || c > '9') + break; + } + + return i; +} + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "PFM"; +} + +static const char * DLL_CALLCONV +Description() { + return "Portable floatmap"; +} + +static const char * DLL_CALLCONV +Extension() { + return "pfm"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/x-portable-floatmap"; +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + BYTE pfm_id1[] = { 0x50, 0x46 }; + BYTE pfm_id2[] = { 0x50, 0x66 }; + BYTE signature[2] = { 0, 0 }; + + io->read_proc(signature, 1, sizeof(pfm_id1), handle); + + if (memcmp(pfm_id1, signature, sizeof(pfm_id1)) == 0) + return TRUE; + + if (memcmp(pfm_id2, signature, sizeof(pfm_id2)) == 0) + return TRUE; + + return FALSE; +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return FALSE; +} + +static BOOL DLL_CALLCONV +SupportsExportType(FREE_IMAGE_TYPE type) { + return ( + (type == FIT_FLOAT) || + (type == FIT_RGBF) + ); +} + +static BOOL DLL_CALLCONV +SupportsNoPixels() { + return TRUE; +} + +// ---------------------------------------------------------- + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + char line_buffer[PFM_MAXLINE]; + char id_one = 0, id_two = 0; + FIBITMAP *dib = NULL; + float *lineBuffer = NULL; + + if (!handle) { + return NULL; + } + + BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + try { + FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; + + // Read the first two bytes of the file to determine the file format + // "PF" = color image + // "Pf" = greyscale image + + io->read_proc(&id_one, 1, 1, handle); + io->read_proc(&id_two, 1, 1, handle); + + if(id_one == 'P') { + if(id_two == 'F') { + image_type = FIT_RGBF; + } else if(id_two == 'f') { + image_type = FIT_FLOAT; + } + } + if(image_type == FIT_UNKNOWN) { + // signature error + throw FI_MSG_ERROR_MAGIC_NUMBER; + } + + // Read the header information: width, height and the scale value + unsigned width = (unsigned) pfm_get_int(io, handle); + unsigned height = (unsigned) pfm_get_int(io, handle); + float scalefactor = 1; + + BOOL bResult = pfm_get_line(io, handle, line_buffer, PFM_MAXLINE); + if(bResult) { + bResult = (sscanf(line_buffer, "%f", &scalefactor) == 1) ? TRUE : FALSE; + } + if (!bResult) { + throw "Read error: invalid PFM header"; + } + + // Create a new DIB + dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height); + if (dib == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + if(header_only) { + // header only mode + return dib; + } + + // Read the image... + + if(image_type == FIT_RGBF) { + const unsigned lineWidth = 3 * width; + lineBuffer = (float*)malloc(lineWidth * sizeof(float)); + if (!lineBuffer) { + throw FI_MSG_ERROR_MEMORY; + } + + for (unsigned y = 0; y < height; y++) { + FIRGBF *bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y); + + if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) { + throw "Read error"; + } + float *channel = lineBuffer; + if(scalefactor > 0) { + // MSB + for (unsigned x = 0; x < width; x++) { + REVERSEBYTES(channel++, &bits[x].red); + REVERSEBYTES(channel++, &bits[x].green); + REVERSEBYTES(channel++, &bits[x].blue); + } + } else { + // LSB + for (unsigned x = 0; x < width; x++) { + bits[x].red = *channel++; + bits[x].green = *channel++; + bits[x].blue = *channel++; + } + } + } + + free(lineBuffer); + lineBuffer = NULL; + + } else if(image_type == FIT_FLOAT) { + const unsigned lineWidth = width; + lineBuffer = (float*)malloc(lineWidth * sizeof(float)); + if (!lineBuffer) { + throw FI_MSG_ERROR_MEMORY; + } + + for (unsigned y = 0; y < height; y++) { + float *bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); + + if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) { + throw "Read error"; + } + float *channel = lineBuffer; + if(scalefactor > 0) { + // MSB - File is Big endian + for (unsigned x = 0; x < width; x++) { + REVERSEBYTES(channel++, &bits[x]); + } + } else { + // LSB - File is Little Endian + for (unsigned x = 0; x < width; x++) { + bits[x] = *channel++; + } + } + } + + free(lineBuffer); + lineBuffer = NULL; + } + + return dib; + + } catch (const char *text) { + if(lineBuffer) free(lineBuffer); + if(dib) FreeImage_Unload(dib); + + if(NULL != text) { + FreeImage_OutputMessageProc(s_format_id, text); + } + + return NULL; + } + +} + +static BOOL DLL_CALLCONV +Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { + if (!dib || !handle) return FALSE; + + FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + if ((image_type != FIT_RGBF) && (image_type != FIT_FLOAT)) { + return FALSE; + } + + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + unsigned lineWidth = FreeImage_GetLine(dib); + + // save image as Little Endian + const float scalefactor = -1.0F; + + char buffer[PFM_MAXLINE]; // temporary buffer whose size should be enough for what we need + + // Find the appropriate magic number for this file type + + char magic = 0; + + switch(image_type) { + case FIT_RGBF: + magic = 'F'; // RGBF + break; + case FIT_FLOAT: + magic = 'f'; // float greyscale + break; + default: + return FALSE; + } + + // Write the header info + + sprintf(buffer, "P%c\n%d %d\n%f\n", magic, width, height, scalefactor); + io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle); + + // Write the image data + for (unsigned y = 0; y < height; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y); + io->write_proc(bits, 1, lineWidth, handle); + } + + return TRUE; +} + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitPFM(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = NULL; + plugin->close_proc = NULL; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = Save; + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = NULL; + plugin->supports_no_pixels_proc = SupportsNoPixels; +} |