/src/FreeImage/Source/FreeImage/PluginHDR.cpp
C++ | 719 lines | 510 code | 98 blank | 111 comment | 124 complexity | 4c308290944fb12d8d53a5a0569e275f MD5 | raw file
1// ========================================================== 2// HDR 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// Plugin Interface 27// ========================================================== 28 29static int s_format_id; 30 31// ========================================================== 32// RGBE library 33// ========================================================== 34 35// ---------------------------------------------------------- 36 37// maximum size of a line in the header 38#define HDR_MAXLINE 256 39 40// flags indicating which fields in an rgbeHeaderInfo are valid 41#define RGBE_VALID_PROGRAMTYPE 0x01 42#define RGBE_VALID_COMMENT 0x02 43#define RGBE_VALID_GAMMA 0x04 44#define RGBE_VALID_EXPOSURE 0x08 45 46// offsets to red, green, and blue components in a data (float) pixel 47#define RGBE_DATA_RED 0 48#define RGBE_DATA_GREEN 1 49#define RGBE_DATA_BLUE 2 50 51// ---------------------------------------------------------- 52#ifdef _WIN32 53#pragma pack(push, 1) 54#else 55#pragma pack(1) 56#endif 57 58typedef struct tagHeaderInfo { 59 int valid; // indicate which fields are valid 60 char programtype[16]; // listed at beginning of file to identify it after "#?". defaults to "RGBE" 61 char comment[HDR_MAXLINE]; // comment beginning with "# " 62 float gamma; // image has already been gamma corrected with given gamma. defaults to 1.0 (no correction) 63 float exposure; // a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0 64} rgbeHeaderInfo; 65 66#ifdef _WIN32 67#pragma pack(pop) 68#else 69#pragma pack() 70#endif 71 72typedef enum { 73 rgbe_read_error, 74 rgbe_write_error, 75 rgbe_format_error, 76 rgbe_memory_error, 77} rgbe_error_code; 78 79// ---------------------------------------------------------- 80// Prototypes 81// ---------------------------------------------------------- 82 83static BOOL rgbe_Error(rgbe_error_code error_code, const char *msg); 84static BOOL rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length); 85static inline void rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf); 86static inline void rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]); 87static BOOL rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info); 88static BOOL rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info); 89static BOOL rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels); 90static BOOL rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels); 91static BOOL rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines); 92static BOOL rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes); 93static BOOL rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines); 94static BOOL rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info); 95static BOOL rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info); 96 97// ---------------------------------------------------------- 98 99/** 100Default error routine. change this to change error handling 101*/ 102static BOOL 103rgbe_Error(rgbe_error_code error_code, const char *msg) { 104 switch (error_code) { 105 case rgbe_read_error: 106 FreeImage_OutputMessageProc(s_format_id, "RGBE read error"); 107 break; 108 case rgbe_write_error: 109 FreeImage_OutputMessageProc(s_format_id, "RGBE write error"); 110 break; 111 case rgbe_format_error: 112 FreeImage_OutputMessageProc(s_format_id, "RGBE bad file format: %s\n", msg); 113 break; 114 default: 115 case rgbe_memory_error: 116 FreeImage_OutputMessageProc(s_format_id, "RGBE error: %s\n",msg); 117 } 118 119 return FALSE; 120} 121 122/** 123Get a line from a ASCII io stream 124*/ 125static BOOL 126rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length) { 127 int i; 128 memset(buffer, 0, length); 129 for(i = 0; i < length; i++) { 130 if(!io->read_proc(&buffer[i], 1, 1, handle)) 131 return FALSE; 132 if(buffer[i] == 0x0A) 133 break; 134 } 135 136 return (i < length) ? TRUE : FALSE; 137} 138 139/** 140Standard conversion from float pixels to rgbe pixels. 141Note: you can remove the "inline"s if your compiler complains about it 142*/ 143static inline void 144rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf) { 145 float v; 146 int e; 147 148 v = rgbf->red; 149 if (rgbf->green > v) v = rgbf->green; 150 if (rgbf->blue > v) v = rgbf->blue; 151 if (v < 1e-32) { 152 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 153 } 154 else { 155 v = (float)(frexp(v, &e) * 256.0 / v); 156 rgbe[0] = (BYTE) (rgbf->red * v); 157 rgbe[1] = (BYTE) (rgbf->green * v); 158 rgbe[2] = (BYTE) (rgbf->blue * v); 159 rgbe[3] = (BYTE) (e + 128); 160 } 161} 162 163/** 164Standard conversion from rgbe to float pixels. 165Note: Ward uses ldexp(col+0.5,exp-(128+8)). 166However we wanted pixels in the range [0,1] to map back into the range [0,1]. 167*/ 168static inline void 169rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]) { 170 if (rgbe[3]) { // nonzero pixel 171 float f = (float)(ldexp(1.0, rgbe[3] - (int)(128+8))); 172 rgbf->red = rgbe[0] * f; 173 rgbf->green = rgbe[1] * f; 174 rgbf->blue = rgbe[2] * f; 175 176 } 177 else { 178 rgbf->red = rgbf->green = rgbf->blue = 0; 179 } 180} 181 182/** 183Minimal header reading. Modify if you want to parse more information 184*/ 185static BOOL 186rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info) { 187 char buf[HDR_MAXLINE]; 188 float tempf; 189 int i; 190 BOOL bFormatFound = FALSE; 191 BOOL bHeaderFound = FALSE; 192 193 header_info->valid = 0; 194 header_info->programtype[0] = 0; 195 header_info->gamma = 1.0; 196 header_info->exposure = 1.0; 197 198 // get the first line 199 if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE)) 200 return rgbe_Error(rgbe_read_error, NULL); 201 202 // check the signature 203 204 if ((buf[0] != '#')||(buf[1] != '?')) { 205 // if you don't want to require the magic token then comment the next line 206 return rgbe_Error(rgbe_format_error,"bad initial token"); 207 } 208 else { 209 header_info->valid |= RGBE_VALID_PROGRAMTYPE; 210 for(i = 0; i < sizeof(header_info->programtype) - 1; i++) { 211 if((buf[i+2] == 0) || isspace(buf[i+2])) 212 break; 213 header_info->programtype[i] = buf[i+2]; 214 } 215 header_info->programtype[i] = 0; 216 } 217 218 for(;;) { 219 // get next line 220 if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE)) 221 return rgbe_Error(rgbe_read_error, NULL); 222 223 if((buf[0] == 0) || (buf[0] == '\n')) { 224 // end of header so break out of loop 225 bHeaderFound = TRUE; 226 break; 227 } 228 else if(strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) { 229 bFormatFound = TRUE; 230 } 231 else if(sscanf(buf, "GAMMA=%g", &tempf) == 1) { 232 header_info->gamma = tempf; 233 header_info->valid |= RGBE_VALID_GAMMA; 234 } 235 else if(sscanf(buf,"EXPOSURE=%g",&tempf) == 1) { 236 header_info->exposure = tempf; 237 header_info->valid |= RGBE_VALID_EXPOSURE; 238 } 239 else if((buf[0] == '#') && (buf[1] == 0x20)) { 240 header_info->valid |= RGBE_VALID_COMMENT; 241 strcpy(header_info->comment, buf); 242 } 243 } 244 if(!bHeaderFound || !bFormatFound) { 245 return rgbe_Error(rgbe_format_error, "invalid header"); 246 } 247 248 // get next line 249 if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE)) 250 return rgbe_Error(rgbe_read_error, NULL); 251 252 // get the image width & height 253 if(sscanf(buf,"-Y %d +X %d", height, width) < 2) { 254 if(sscanf(buf,"+X %d +Y %d", height, width) < 2) { 255 return rgbe_Error(rgbe_format_error, "missing image size specifier"); 256 } 257 } 258 259 return TRUE; 260} 261 262/** 263 default minimal header. modify if you want more information in header 264*/ 265static BOOL 266rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info) { 267 char buffer[HDR_MAXLINE]; 268 269 const char *programtype = "RADIANCE"; 270 271 if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) { 272 programtype = info->programtype; 273 } 274 // The #? is to identify file type, the programtype is optional 275 sprintf(buffer, "#?%s\n", programtype); 276 if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1) 277 return rgbe_Error(rgbe_write_error, NULL); 278 sprintf(buffer, "%s\n", info->comment); 279 if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1) 280 return rgbe_Error(rgbe_write_error, NULL); 281 sprintf(buffer, "FORMAT=32-bit_rle_rgbe\n"); 282 if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1) 283 return rgbe_Error(rgbe_write_error, NULL); 284 if(info && (info->valid & RGBE_VALID_GAMMA)) { 285 sprintf(buffer, "GAMMA=%g\n", info->gamma); 286 if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1) 287 return rgbe_Error(rgbe_write_error, NULL); 288 } 289 if(info && (info->valid & RGBE_VALID_EXPOSURE)) { 290 sprintf(buffer,"EXPOSURE=%g\n", info->exposure); 291 if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1) 292 return rgbe_Error(rgbe_write_error, NULL); 293 } 294 sprintf(buffer, "\n-Y %d +X %d\n", height, width); 295 if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1) 296 return rgbe_Error(rgbe_write_error, NULL); 297 298 return TRUE; 299} 300 301static BOOL 302rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) { 303 return TRUE; 304} 305static BOOL 306rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) { 307 header_info->gamma = 1; 308 header_info->valid |= RGBE_VALID_GAMMA; 309 header_info->exposure = 0; 310 header_info->valid |= RGBE_VALID_EXPOSURE; 311 312 return TRUE; 313} 314 315/** 316Simple read routine. Will not correctly handle run length encoding 317*/ 318static BOOL 319rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) { 320 BYTE rgbe[4]; 321 322 for(unsigned x = 0; x < numpixels; x++) { 323 if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) { 324 return rgbe_Error(rgbe_read_error, NULL); 325 } 326 rgbe_RGBEToFloat(&data[x], rgbe); 327 } 328 329 return TRUE; 330} 331 332/** 333 Simple write routine that does not use run length encoding. 334 These routines can be made faster by allocating a larger buffer and 335 fread-ing and fwrite-ing the data in larger chunks. 336*/ 337static BOOL 338rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) { 339 BYTE rgbe[4]; 340 341 for(unsigned x = 0; x < numpixels; x++) { 342 rgbe_FloatToRGBE(rgbe, &data[x]); 343 if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1) 344 return rgbe_Error(rgbe_write_error, NULL); 345 } 346 347 return TRUE; 348} 349 350static BOOL 351rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines) { 352 BYTE rgbe[4], *scanline_buffer, *ptr, *ptr_end; 353 int i, count; 354 BYTE buf[2]; 355 356 if ((scanline_width < 8)||(scanline_width > 0x7fff)) { 357 // run length encoding is not allowed so read flat 358 return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines); 359 } 360 scanline_buffer = NULL; 361 // read in each successive scanline 362 while(num_scanlines > 0) { 363 if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) { 364 free(scanline_buffer); 365 return rgbe_Error(rgbe_read_error,NULL); 366 } 367 if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) { 368 // this file is not run length encoded 369 rgbe_RGBEToFloat(data, rgbe); 370 data ++; 371 free(scanline_buffer); 372 return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines - 1); 373 } 374 if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) { 375 free(scanline_buffer); 376 return rgbe_Error(rgbe_format_error,"wrong scanline width"); 377 } 378 if(scanline_buffer == NULL) { 379 scanline_buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width); 380 if(scanline_buffer == NULL) { 381 return rgbe_Error(rgbe_memory_error, "unable to allocate buffer space"); 382 } 383 } 384 385 ptr = &scanline_buffer[0]; 386 // read each of the four channels for the scanline into the buffer 387 for(i = 0; i < 4; i++) { 388 ptr_end = &scanline_buffer[(i+1)*scanline_width]; 389 while(ptr < ptr_end) { 390 if(io->read_proc(buf, 1, 2 * sizeof(BYTE), handle) < 1) { 391 free(scanline_buffer); 392 return rgbe_Error(rgbe_read_error, NULL); 393 } 394 if(buf[0] > 128) { 395 // a run of the same value 396 count = buf[0] - 128; 397 if((count == 0) || (count > ptr_end - ptr)) { 398 free(scanline_buffer); 399 return rgbe_Error(rgbe_format_error, "bad scanline data"); 400 } 401 while(count-- > 0) 402 *ptr++ = buf[1]; 403 } 404 else { 405 // a non-run 406 count = buf[0]; 407 if((count == 0) || (count > ptr_end - ptr)) { 408 free(scanline_buffer); 409 return rgbe_Error(rgbe_format_error, "bad scanline data"); 410 } 411 *ptr++ = buf[1]; 412 if(--count > 0) { 413 if(io->read_proc(ptr, 1, sizeof(BYTE) * count, handle) < 1) { 414 free(scanline_buffer); 415 return rgbe_Error(rgbe_read_error, NULL); 416 } 417 ptr += count; 418 } 419 } 420 } 421 } 422 // now convert data from buffer into floats 423 for(i = 0; i < scanline_width; i++) { 424 rgbe[0] = scanline_buffer[i]; 425 rgbe[1] = scanline_buffer[i+scanline_width]; 426 rgbe[2] = scanline_buffer[i+2*scanline_width]; 427 rgbe[3] = scanline_buffer[i+3*scanline_width]; 428 rgbe_RGBEToFloat(data, rgbe); 429 data ++; 430 } 431 432 num_scanlines--; 433 } 434 435 free(scanline_buffer); 436 437 return TRUE; 438} 439 440/** 441 The code below is only needed for the run-length encoded files. 442 Run length encoding adds considerable complexity but does 443 save some space. For each scanline, each channel (r,g,b,e) is 444 encoded separately for better compression. 445 @return Returns TRUE if successful, returns FALSE otherwise 446*/ 447static BOOL 448rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes) { 449 static const int MINRUNLENGTH = 4; 450 int cur, beg_run, run_count, old_run_count, nonrun_count; 451 BYTE buf[2]; 452 453 cur = 0; 454 while(cur < numbytes) { 455 beg_run = cur; 456 // find next run of length at least 4 if one exists 457 run_count = old_run_count = 0; 458 while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { 459 beg_run += run_count; 460 old_run_count = run_count; 461 run_count = 1; 462 while((data[beg_run] == data[beg_run + run_count]) && (beg_run + run_count < numbytes) && (run_count < 127)) 463 run_count++; 464 } 465 // if data before next big run is a short run then write it as such 466 if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) { 467 buf[0] = (BYTE)(128 + old_run_count); // write short run 468 buf[1] = data[cur]; 469 if(io->write_proc(buf, 2 * sizeof(BYTE), 1, handle) < 1) 470 return rgbe_Error(rgbe_write_error, NULL); 471 cur = beg_run; 472 } 473 // write out bytes until we reach the start of the next run 474 while(cur < beg_run) { 475 nonrun_count = beg_run - cur; 476 if (nonrun_count > 128) 477 nonrun_count = 128; 478 buf[0] = (BYTE)nonrun_count; 479 if(io->write_proc(buf, sizeof(buf[0]), 1, handle) < 1) 480 return rgbe_Error(rgbe_write_error,NULL); 481 if(io->write_proc(&data[cur], sizeof(data[0]) * nonrun_count, 1, handle) < 1) 482 return rgbe_Error(rgbe_write_error,NULL); 483 cur += nonrun_count; 484 } 485 // write out next run if one was found 486 if (run_count >= MINRUNLENGTH) { 487 buf[0] = (BYTE)(128 + run_count); 488 buf[1] = data[beg_run]; 489 if(io->write_proc(buf, sizeof(buf[0]) * 2, 1, handle) < 1) 490 return rgbe_Error(rgbe_write_error,NULL); 491 cur += run_count; 492 } 493 } 494 495 return TRUE; 496} 497 498static BOOL 499rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines) { 500 BYTE rgbe[4]; 501 BYTE *buffer; 502 503 if ((scanline_width < 8)||(scanline_width > 0x7fff)) { 504 // run length encoding is not allowed so write flat 505 return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines); 506 } 507 buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width); 508 if (buffer == NULL) { 509 // no buffer space so write flat 510 return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines); 511 } 512 while(num_scanlines-- > 0) { 513 rgbe[0] = (BYTE)2; 514 rgbe[1] = (BYTE)2; 515 rgbe[2] = (BYTE)(scanline_width >> 8); 516 rgbe[3] = (BYTE)(scanline_width & 0xFF); 517 if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1) { 518 free(buffer); 519 return rgbe_Error(rgbe_write_error, NULL); 520 } 521 for(unsigned x = 0; x < scanline_width; x++) { 522 rgbe_FloatToRGBE(rgbe, data); 523 buffer[x] = rgbe[0]; 524 buffer[x+scanline_width] = rgbe[1]; 525 buffer[x+2*scanline_width] = rgbe[2]; 526 buffer[x+3*scanline_width] = rgbe[3]; 527 data ++; 528 } 529 // write out each of the four channels separately run length encoded 530 // first red, then green, then blue, then exponent 531 for(int i = 0; i < 4; i++) { 532 BOOL bOK = rgbe_WriteBytes_RLE(io, handle, &buffer[i*scanline_width], scanline_width); 533 if(!bOK) { 534 free(buffer); 535 return bOK; 536 } 537 } 538 } 539 free(buffer); 540 541 return TRUE; 542} 543 544 545// ---------------------------------------------------------- 546 547 548 549// ========================================================== 550// Plugin Implementation 551// ========================================================== 552 553static const char * DLL_CALLCONV 554Format() { 555 return "HDR"; 556} 557 558static const char * DLL_CALLCONV 559Description() { 560 return "High Dynamic Range Image"; 561} 562 563static const char * DLL_CALLCONV 564Extension() { 565 return "hdr"; 566} 567 568static const char * DLL_CALLCONV 569RegExpr() { 570 return NULL; 571} 572 573static const char * DLL_CALLCONV 574MimeType() { 575 return "image/vnd.radiance"; 576} 577 578static BOOL DLL_CALLCONV 579Validate(FreeImageIO *io, fi_handle handle) { 580 BYTE hdr_signature[] = { '#', '?' }; 581 BYTE signature[] = { 0, 0 }; 582 583 io->read_proc(signature, 1, 2, handle); 584 585 return (memcmp(hdr_signature, signature, 2) == 0); 586} 587 588static BOOL DLL_CALLCONV 589SupportsExportDepth(int depth) { 590 return FALSE; 591} 592 593static BOOL DLL_CALLCONV 594SupportsExportType(FREE_IMAGE_TYPE type) { 595 return (type == FIT_RGBF) ? TRUE : FALSE; 596} 597 598static BOOL DLL_CALLCONV 599SupportsNoPixels() { 600 return TRUE; 601} 602 603// -------------------------------------------------------------------------- 604 605static FIBITMAP * DLL_CALLCONV 606Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { 607 FIBITMAP *dib = NULL; 608 609 if(!handle) { 610 return NULL; 611 } 612 613 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; 614 615 try { 616 617 rgbeHeaderInfo header_info; 618 unsigned width, height; 619 620 // Read the header 621 if(rgbe_ReadHeader(io, handle, &width, &height, &header_info) == FALSE) { 622 return NULL; 623 } 624 625 // allocate a RGBF image 626 dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBF, width, height); 627 if(!dib) { 628 throw FI_MSG_ERROR_MEMORY; 629 } 630 631 // set the metadata as comments 632 rgbe_ReadMetadata(dib, &header_info); 633 634 if(header_only) { 635 // header only mode 636 return dib; 637 } 638 639 // read the image pixels and fill the dib 640 641 for(unsigned y = 0; y < height; y++) { 642 FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y); 643 if(!rgbe_ReadPixels_RLE(io, handle, scanline, width, 1)) { 644 FreeImage_Unload(dib); 645 return NULL; 646 } 647 } 648 649 } 650 catch(const char *text) { 651 if(dib != NULL) { 652 FreeImage_Unload(dib); 653 } 654 FreeImage_OutputMessageProc(s_format_id, text); 655 } 656 657 return dib; 658} 659 660static BOOL DLL_CALLCONV 661Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { 662 if(!dib) return FALSE; 663 664 if(FreeImage_GetImageType(dib) != FIT_RGBF) { 665 return FALSE; 666 } 667 668 unsigned width = FreeImage_GetWidth(dib); 669 unsigned height = FreeImage_GetHeight(dib); 670 671 // write the header 672 673 rgbeHeaderInfo header_info; 674 memset(&header_info, 0, sizeof(rgbeHeaderInfo)); 675 // fill the header with correct gamma and exposure 676 rgbe_WriteMetadata(dib, &header_info); 677 // fill a comment 678 sprintf(header_info.comment, "# Made with FreeImage %s", FreeImage_GetVersion()); 679 if(!rgbe_WriteHeader(io, handle, width, height, &header_info)) { 680 return FALSE; 681 } 682 683 // write each scanline 684 685 for(unsigned y = 0; y < height; y++) { 686 FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y); 687 if(!rgbe_WritePixels_RLE(io, handle, scanline, width, 1)) { 688 return FALSE; 689 } 690 } 691 692 return TRUE; 693} 694 695// ========================================================== 696// Init 697// ========================================================== 698 699void DLL_CALLCONV 700InitHDR(Plugin *plugin, int format_id) { 701 s_format_id = format_id; 702 703 plugin->format_proc = Format; 704 plugin->description_proc = Description; 705 plugin->extension_proc = Extension; 706 plugin->regexpr_proc = RegExpr; 707 plugin->open_proc = NULL; 708 plugin->close_proc = NULL; 709 plugin->pagecount_proc = NULL; 710 plugin->pagecapability_proc = NULL; 711 plugin->load_proc = Load; 712 plugin->save_proc = Save; 713 plugin->validate_proc = Validate; 714 plugin->mime_proc = MimeType; 715 plugin->supports_export_bpp_proc = SupportsExportDepth; 716 plugin->supports_export_type_proc = SupportsExportType; 717 plugin->supports_icc_profiles_proc = NULL; 718 plugin->supports_no_pixels_proc = SupportsNoPixels; 719}