/src/FreeImage/Source/FreeImage/PluginPFM.cpp
C++ | 402 lines | 258 code | 81 blank | 63 comment | 78 complexity | 38dc9573ee3732b4f4a0acd8dd777887 MD5 | raw file
1// ========================================================== 2// PFM Loader and Writer 3// 4// Design and implementation by 5// - Hervé Drolon (drolon@infonie.fr) 6// 7// This file is part of FreeImage 3 8// 9// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY 10// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES 11// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 12// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED 13// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT 14// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY 15// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL 16// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 17// THIS DISCLAIMER. 18// 19// Use at your own risk! 20// ========================================================== 21 22#include "FreeImage.h" 23#include "Utilities.h" 24 25// ========================================================== 26// Internal functions 27// ========================================================== 28 29/** maximum size of a line in the header */ 30#define PFM_MAXLINE 256 31 32/** Big endian / Little endian float conversion */ 33#define REVERSEBYTES(source, dest) \ 34{ \ 35 char *j = (char *) source; \ 36 char *dj = (char *) dest; \ 37 dj[0] = j[3]; \ 38 dj[1] = j[2]; \ 39 dj[2] = j[1]; \ 40 dj[3] = j[0]; \ 41} 42 43/** 44Get a line from a ASCII io stream 45*/ 46static BOOL 47pfm_get_line(FreeImageIO *io, fi_handle handle, char *buffer, int length) { 48 int i; 49 memset(buffer, 0, length); 50 for(i = 0; i < length; i++) { 51 if(!io->read_proc(&buffer[i], 1, 1, handle)) 52 return FALSE; 53 if(buffer[i] == 0x0A) 54 break; 55 } 56 57 return (i < length) ? TRUE : FALSE; 58} 59 60/** 61Get an integer value from the actual position pointed by handle 62*/ 63static int 64pfm_get_int(FreeImageIO *io, fi_handle handle) { 65 char c = 0; 66 BOOL firstchar; 67 68 // skip forward to start of next number 69 70 if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; 71 72 while (1) { 73 // eat comments 74 75 if (c == '#') { 76 // if we're at a comment, read to end of line 77 78 firstchar = TRUE; 79 80 while (1) { 81 if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; 82 83 if (firstchar && c == ' ') { 84 // loop off 1 sp after # 85 86 firstchar = FALSE; 87 } else if (c == '\n') { 88 break; 89 } 90 } 91 } 92 93 if (c >= '0' && c <='9') { 94 // we've found what we were looking for 95 96 break; 97 } 98 99 if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; 100 } 101 102 // we're at the start of a number, continue until we hit a non-number 103 104 int i = 0; 105 106 while (1) { 107 i = (i * 10) + (c - '0'); 108 109 if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; 110 111 if (c < '0' || c > '9') 112 break; 113 } 114 115 return i; 116} 117 118// ========================================================== 119// Plugin Interface 120// ========================================================== 121 122static int s_format_id; 123 124// ========================================================== 125// Plugin Implementation 126// ========================================================== 127 128static const char * DLL_CALLCONV 129Format() { 130 return "PFM"; 131} 132 133static const char * DLL_CALLCONV 134Description() { 135 return "Portable floatmap"; 136} 137 138static const char * DLL_CALLCONV 139Extension() { 140 return "pfm"; 141} 142 143static const char * DLL_CALLCONV 144RegExpr() { 145 return NULL; 146} 147 148static const char * DLL_CALLCONV 149MimeType() { 150 return "image/x-portable-floatmap"; 151} 152 153static BOOL DLL_CALLCONV 154Validate(FreeImageIO *io, fi_handle handle) { 155 BYTE pfm_id1[] = { 0x50, 0x46 }; 156 BYTE pfm_id2[] = { 0x50, 0x66 }; 157 BYTE signature[2] = { 0, 0 }; 158 159 io->read_proc(signature, 1, sizeof(pfm_id1), handle); 160 161 if (memcmp(pfm_id1, signature, sizeof(pfm_id1)) == 0) 162 return TRUE; 163 164 if (memcmp(pfm_id2, signature, sizeof(pfm_id2)) == 0) 165 return TRUE; 166 167 return FALSE; 168} 169 170static BOOL DLL_CALLCONV 171SupportsExportDepth(int depth) { 172 return FALSE; 173} 174 175static BOOL DLL_CALLCONV 176SupportsExportType(FREE_IMAGE_TYPE type) { 177 return ( 178 (type == FIT_FLOAT) || 179 (type == FIT_RGBF) 180 ); 181} 182 183static BOOL DLL_CALLCONV 184SupportsNoPixels() { 185 return TRUE; 186} 187 188// ---------------------------------------------------------- 189 190static FIBITMAP * DLL_CALLCONV 191Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { 192 char line_buffer[PFM_MAXLINE]; 193 char id_one = 0, id_two = 0; 194 FIBITMAP *dib = NULL; 195 float *lineBuffer = NULL; 196 197 if (!handle) { 198 return NULL; 199 } 200 201 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; 202 203 try { 204 FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; 205 206 // Read the first two bytes of the file to determine the file format 207 // "PF" = color image 208 // "Pf" = greyscale image 209 210 io->read_proc(&id_one, 1, 1, handle); 211 io->read_proc(&id_two, 1, 1, handle); 212 213 if(id_one == 'P') { 214 if(id_two == 'F') { 215 image_type = FIT_RGBF; 216 } else if(id_two == 'f') { 217 image_type = FIT_FLOAT; 218 } 219 } 220 if(image_type == FIT_UNKNOWN) { 221 // signature error 222 throw FI_MSG_ERROR_MAGIC_NUMBER; 223 } 224 225 // Read the header information: width, height and the scale value 226 unsigned width = (unsigned) pfm_get_int(io, handle); 227 unsigned height = (unsigned) pfm_get_int(io, handle); 228 float scalefactor = 1; 229 230 BOOL bResult = pfm_get_line(io, handle, line_buffer, PFM_MAXLINE); 231 if(bResult) { 232 bResult = (sscanf(line_buffer, "%f", &scalefactor) == 1) ? TRUE : FALSE; 233 } 234 if(!bResult) { 235 throw "Read error: invalid PFM header"; 236 } 237 238 // Create a new DIB 239 dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height); 240 if (dib == NULL) { 241 throw FI_MSG_ERROR_DIB_MEMORY; 242 } 243 244 if(header_only) { 245 // header only mode 246 return dib; 247 } 248 249 // Read the image... 250 251 if(image_type == FIT_RGBF) { 252 const unsigned lineWidth = 3 * width; 253 lineBuffer = (float*)malloc(lineWidth * sizeof(float)); 254 if(!lineBuffer) { 255 throw FI_MSG_ERROR_MEMORY; 256 } 257 258 for (unsigned y = 0; y < height; y++) { 259 FIRGBF *bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y); 260 261 if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) { 262 throw "Read error"; 263 } 264 float *channel = lineBuffer; 265 if(scalefactor > 0) { 266 // MSB 267 for (unsigned x = 0; x < width; x++) { 268 REVERSEBYTES(channel++, &bits[x].red); 269 REVERSEBYTES(channel++, &bits[x].green); 270 REVERSEBYTES(channel++, &bits[x].blue); 271 } 272 } else { 273 // LSB 274 for (unsigned x = 0; x < width; x++) { 275 bits[x].red = *channel++; 276 bits[x].green = *channel++; 277 bits[x].blue = *channel++; 278 } 279 } 280 } 281 282 free(lineBuffer); 283 lineBuffer = NULL; 284 285 } else if(image_type == FIT_FLOAT) { 286 const unsigned lineWidth = width; 287 lineBuffer = (float*)malloc(lineWidth * sizeof(float)); 288 if(!lineBuffer) { 289 throw FI_MSG_ERROR_MEMORY; 290 } 291 292 for (unsigned y = 0; y < height; y++) { 293 float *bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); 294 295 if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) { 296 throw "Read error"; 297 } 298 float *channel = lineBuffer; 299 if(scalefactor > 0) { 300 // MSB - File is Big endian 301 for (unsigned x = 0; x < width; x++) { 302 REVERSEBYTES(channel++, &bits[x]); 303 } 304 } else { 305 // LSB - File is Little Endian 306 for (unsigned x = 0; x < width; x++) { 307 bits[x] = *channel++; 308 } 309 } 310 } 311 312 free(lineBuffer); 313 lineBuffer = NULL; 314 } 315 316 return dib; 317 318 } catch (const char *text) { 319 if(lineBuffer) free(lineBuffer); 320 if(dib) FreeImage_Unload(dib); 321 322 if(NULL != text) { 323 FreeImage_OutputMessageProc(s_format_id, text); 324 } 325 326 return NULL; 327 } 328 329} 330 331static BOOL DLL_CALLCONV 332Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { 333 if(!dib || !handle) return FALSE; 334 335 FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); 336 if((image_type != FIT_RGBF) && (image_type != FIT_FLOAT)) { 337 return FALSE; 338 } 339 340 unsigned width = FreeImage_GetWidth(dib); 341 unsigned height = FreeImage_GetHeight(dib); 342 unsigned lineWidth = FreeImage_GetLine(dib); 343 344 // save image as Little Endian 345 const float scalefactor = -1.0F; 346 347 char buffer[PFM_MAXLINE]; // temporary buffer whose size should be enough for what we need 348 349 // Find the appropriate magic number for this file type 350 351 char magic = 0; 352 353 switch(image_type) { 354 case FIT_RGBF: 355 magic = 'F'; // RGBF 356 break; 357 case FIT_FLOAT: 358 magic = 'f'; // float greyscale 359 break; 360 default: 361 return FALSE; 362 } 363 364 // Write the header info 365 366 sprintf(buffer, "P%c\n%d %d\n%f\n", magic, width, height, scalefactor); 367 io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle); 368 369 // Write the image data 370 for (unsigned y = 0; y < height; y++) { 371 BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y); 372 io->write_proc(bits, 1, lineWidth, handle); 373 } 374 375 return TRUE; 376} 377 378// ========================================================== 379// Init 380// ========================================================== 381 382void DLL_CALLCONV 383InitPFM(Plugin *plugin, int format_id) { 384 s_format_id = format_id; 385 386 plugin->format_proc = Format; 387 plugin->description_proc = Description; 388 plugin->extension_proc = Extension; 389 plugin->regexpr_proc = RegExpr; 390 plugin->open_proc = NULL; 391 plugin->close_proc = NULL; 392 plugin->pagecount_proc = NULL; 393 plugin->pagecapability_proc = NULL; 394 plugin->load_proc = Load; 395 plugin->save_proc = Save; 396 plugin->validate_proc = Validate; 397 plugin->mime_proc = MimeType; 398 plugin->supports_export_bpp_proc = SupportsExportDepth; 399 plugin->supports_export_type_proc = SupportsExportType; 400 plugin->supports_icc_profiles_proc = NULL; 401 plugin->supports_no_pixels_proc = SupportsNoPixels; 402}