/src/FreeImage/Source/FreeImage/PluginEXR.cpp
C++ | 764 lines | 536 code | 123 blank | 105 comment | 146 complexity | 4f247065f1f01082596bba24ed7e0633 MD5 | raw file
1// ========================================================== 2// EXR Loader and writer 3// 4// Design and implementation by 5// - Hervé Drolon (drolon@infonie.fr) 6// - Mihail Naydenov (mnaydenov@users.sourceforge.net) 7// 8// This file is part of FreeImage 3 9// 10// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY 11// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES 12// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 13// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED 14// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT 15// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY 16// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL 17// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 18// THIS DISCLAIMER. 19// 20// Use at your own risk! 21// ========================================================== 22 23#include "FreeImage.h" 24#include "Utilities.h" 25#include "../OpenEXR/IlmImf/ImfIO.h" 26#include "../OpenEXR/Iex/Iex.h" 27#include "../OpenEXR/IlmImf/ImfOutputFile.h" 28#include "../OpenEXR/IlmImf/ImfInputFile.h" 29#include "../OpenEXR/IlmImf/ImfRgbaFile.h" 30#include "../OpenEXR/IlmImf/ImfChannelList.h" 31#include "../OpenEXR/IlmImf/ImfRgba.h" 32#include "../OpenEXR/IlmImf/ImfArray.h" 33#include "../OpenEXR/IlmImf/ImfPreviewImage.h" 34#include "../OpenEXR/Half/half.h" 35 36 37// ========================================================== 38// Plugin Interface 39// ========================================================== 40 41static int s_format_id; 42 43// ---------------------------------------------------------- 44 45/** 46FreeImage input stream wrapper 47*/ 48class C_IStream: public Imf::IStream { 49public: 50 C_IStream (FreeImageIO *io, fi_handle handle): 51 IStream(""), _io (io), _handle(handle) {} 52 53 virtual bool read (char c[/*n*/], int n); 54 virtual Imf::Int64 tellg (); 55 virtual void seekg (Imf::Int64 pos); 56 virtual void clear () {}; 57 58private: 59 FreeImageIO *_io; 60 fi_handle _handle; 61}; 62 63 64/** 65FreeImage output stream wrapper 66*/ 67class C_OStream: public Imf::OStream { 68public: 69 C_OStream (FreeImageIO *io, fi_handle handle): 70 OStream(""), _io (io), _handle(handle) {} 71 72 virtual void write (const char c[/*n*/], int n); 73 virtual Imf::Int64 tellp (); 74 virtual void seekp (Imf::Int64 pos); 75 76private: 77 FreeImageIO *_io; 78 fi_handle _handle; 79}; 80 81 82bool 83C_IStream::read (char c[/*n*/], int n) { 84 return ((unsigned)n != _io->read_proc(c, 1, n, _handle)); 85} 86 87Imf::Int64 88C_IStream::tellg () { 89 return _io->tell_proc(_handle); 90} 91 92void 93C_IStream::seekg (Imf::Int64 pos) { 94 _io->seek_proc(_handle, (unsigned)pos, SEEK_SET); 95} 96 97void 98C_OStream::write (const char c[/*n*/], int n) { 99 if((unsigned)n != _io->write_proc((void*)&c[0], 1, n, _handle)) { 100 Iex::throwErrnoExc(); 101 } 102} 103 104Imf::Int64 105C_OStream::tellp () { 106 return _io->tell_proc(_handle); 107} 108 109void 110C_OStream::seekp (Imf::Int64 pos) { 111 _io->seek_proc(_handle, (unsigned)pos, SEEK_SET); 112} 113 114// ---------------------------------------------------------- 115 116 117// ========================================================== 118// Plugin Implementation 119// ========================================================== 120 121static const char * DLL_CALLCONV 122Format() { 123 return "EXR"; 124} 125 126static const char * DLL_CALLCONV 127Description() { 128 return "ILM OpenEXR"; 129} 130 131static const char * DLL_CALLCONV 132Extension() { 133 return "exr"; 134} 135 136static const char * DLL_CALLCONV 137RegExpr() { 138 return NULL; 139} 140 141static const char * DLL_CALLCONV 142MimeType() { 143 return "image/x-exr"; 144} 145 146static BOOL DLL_CALLCONV 147Validate(FreeImageIO *io, fi_handle handle) { 148 BYTE exr_signature[] = { 0x76, 0x2F, 0x31, 0x01 }; 149 BYTE signature[] = { 0, 0, 0, 0 }; 150 151 io->read_proc(signature, 1, 4, handle); 152 return (memcmp(exr_signature, signature, 4) == 0); 153} 154 155static BOOL DLL_CALLCONV 156SupportsExportDepth(int depth) { 157 return FALSE; 158} 159 160static BOOL DLL_CALLCONV 161SupportsExportType(FREE_IMAGE_TYPE type) { 162 return ( 163 (type == FIT_FLOAT) || 164 (type == FIT_RGBF) || 165 (type == FIT_RGBAF) 166 ); 167} 168 169static BOOL DLL_CALLCONV 170SupportsNoPixels() { 171 return TRUE; 172} 173 174// -------------------------------------------------------------------------- 175 176static FIBITMAP * DLL_CALLCONV 177Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { 178 bool bUseRgbaInterface = false; 179 FIBITMAP *dib = NULL; 180 181 if(!handle) { 182 return NULL; 183 } 184 185 try { 186 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; 187 188 // save the stream starting point 189 const long stream_start = io->tell_proc(handle); 190 191 // wrap the FreeImage IO stream 192 C_IStream istream(io, handle); 193 194 // open the file 195 Imf::InputFile file(istream); 196 197 // get file info 198 const Imath::Box2i &dataWindow = file.header().dataWindow(); 199 int width = dataWindow.max.x - dataWindow.min.x + 1; 200 int height = dataWindow.max.y - dataWindow.min.y + 1; 201 202 //const Imf::Compression &compression = file.header().compression(); 203 204 const Imf::ChannelList &channels = file.header().channels(); 205 206 // check the number of components and check for a coherent format 207 208 std::string exr_color_model; 209 Imf::PixelType pixel_type = Imf::HALF; 210 FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; 211 int components = 0; 212 bool bMixedComponents = false; 213 214 for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { 215 components++; 216 if(components == 1) { 217 exr_color_model += i.name(); 218 pixel_type = i.channel().type; 219 } else { 220 exr_color_model += "/"; 221 exr_color_model += i.name(); 222 if (i.channel().type != pixel_type) { 223 bMixedComponents = true; 224 } 225 } 226 } 227 228 if(bMixedComponents) { 229 bool bHandled = false; 230 // we may have a RGBZ or RGBAZ image ... 231 if(components > 4) { 232 if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B") && channels.findChannel("A")) { 233 std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model"; 234 FreeImage_OutputMessageProc(s_format_id, msg.c_str()); 235 bHandled = true; 236 } 237 } 238 else if(components > 3) { 239 if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { 240 std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model"; 241 FreeImage_OutputMessageProc(s_format_id, msg.c_str()); 242 bHandled = true; 243 } 244 } 245 if(!bHandled) { 246 THROW (Iex::InputExc, "Unable to handle mixed component types (color model = " << exr_color_model << ")"); 247 } 248 } 249 250 switch(pixel_type) { 251 case Imf::UINT: 252 THROW (Iex::InputExc, "Unsupported format: UINT"); 253 break; 254 case Imf::HALF: 255 case Imf::FLOAT: 256 default: 257 break; 258 } 259 260 // check for supported image color models 261 // -------------------------------------------------------------- 262 263 if((components == 1) || (components == 2)) { 264 // if the image is gray-alpha (YA), ignore the alpha channel 265 if((components == 1) && channels.findChannel("Y")) { 266 image_type = FIT_FLOAT; 267 components = 1; 268 } else { 269 std::string msg = "Warning: loading color model " + exr_color_model + " as Y color model"; 270 FreeImage_OutputMessageProc(s_format_id, msg.c_str()); 271 image_type = FIT_FLOAT; 272 // ignore the other channel 273 components = 1; 274 } 275 } else if(components == 3) { 276 if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { 277 image_type = FIT_RGBF; 278 } 279 else if(channels.findChannel("BY") && channels.findChannel("RY") && channels.findChannel("Y")) { 280 image_type = FIT_RGBF; 281 bUseRgbaInterface = true; 282 } 283 } else if(components >= 4) { 284 if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { 285 if(channels.findChannel("A")) { 286 if(components > 4) { 287 std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model"; 288 FreeImage_OutputMessageProc(s_format_id, msg.c_str()); 289 } 290 image_type = FIT_RGBAF; 291 // ignore other layers if there is more than one alpha layer 292 components = 4; 293 } else { 294 std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model"; 295 FreeImage_OutputMessageProc(s_format_id, msg.c_str()); 296 297 image_type = FIT_RGBF; 298 // ignore other channels 299 components = 3; 300 } 301 } 302 } 303 304 if(image_type == FIT_UNKNOWN) { 305 THROW (Iex::InputExc, "Unsupported color model: " << exr_color_model); 306 } 307 308 // allocate a new dib 309 dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, 0); 310 if(!dib) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); 311 312 // try to load the preview image 313 // -------------------------------------------------------------- 314 315 if(file.header().hasPreviewImage()) { 316 const Imf::PreviewImage& preview = file.header().previewImage(); 317 const unsigned thWidth = preview.width(); 318 const unsigned thHeight = preview.height(); 319 320 FIBITMAP* thumbnail = FreeImage_Allocate(thWidth, thHeight, 32); 321 if(thumbnail) { 322 const Imf::PreviewRgba *src_line = preview.pixels(); 323 BYTE *dst_line = FreeImage_GetScanLine(thumbnail, thHeight - 1); 324 const unsigned dstPitch = FreeImage_GetPitch(thumbnail); 325 326 for (unsigned y = 0; y < thHeight; ++y) { 327 const Imf::PreviewRgba *src_pixel = src_line; 328 RGBQUAD* dst_pixel = (RGBQUAD*)dst_line; 329 330 for(unsigned x = 0; x < thWidth; ++x) { 331 dst_pixel->rgbRed = src_pixel->r; 332 dst_pixel->rgbGreen = src_pixel->g; 333 dst_pixel->rgbBlue = src_pixel->b; 334 dst_pixel->rgbReserved = src_pixel->a; 335 src_pixel++; 336 dst_pixel++; 337 } 338 src_line += thWidth; 339 dst_line -= dstPitch; 340 } 341 FreeImage_SetThumbnail(dib, thumbnail); 342 FreeImage_Unload(thumbnail); 343 } 344 } 345 346 if(header_only) { 347 // header only mode 348 return dib; 349 } 350 351 // load pixels 352 // -------------------------------------------------------------- 353 354 const BYTE *bits = FreeImage_GetBits(dib); // pointer to our pixel buffer 355 const size_t bytespp = sizeof(float) * components; // size of our pixel in bytes 356 const unsigned pitch = FreeImage_GetPitch(dib); // size of our yStride in bytes 357 358 Imf::PixelType pixelType = Imf::FLOAT; // load as float data type; 359 360 if(bUseRgbaInterface) { 361 // use the RGBA interface (used when loading RY BY Y images ) 362 363 const int chunk_size = 16; 364 365 BYTE *scanline = (BYTE*)bits; 366 367 // re-open using the RGBA interface 368 io->seek_proc(handle, stream_start, SEEK_SET); 369 Imf::RgbaInputFile rgbaFile(istream); 370 371 // read the file in chunks 372 Imath::Box2i dw = dataWindow; 373 Imf::Array2D<Imf::Rgba> chunk(chunk_size, width); 374 while (dw.min.y <= dw.max.y) { 375 // read a chunk 376 rgbaFile.setFrameBuffer (&chunk[0][0] - dw.min.x - dw.min.y * width, 1, width); 377 rgbaFile.readPixels (dw.min.y, MIN(dw.min.y + chunk_size - 1, dw.max.y)); 378 // fill the dib 379 const int y_max = ((dw.max.y - dw.min.y) <= chunk_size) ? (dw.max.y - dw.min.y) : chunk_size; 380 for(int y = 0; y < y_max; y++) { 381 FIRGBF *pixel = (FIRGBF*)scanline; 382 const Imf::Rgba *half_rgba = chunk[y]; 383 for(int x = 0; x < width; x++) { 384 // convert from half to float 385 pixel[x].red = half_rgba[x].r; 386 pixel[x].green = half_rgba[x].g; 387 pixel[x].blue = half_rgba[x].b; 388 } 389 // next line 390 scanline += pitch; 391 } 392 // next chunk 393 dw.min.y += chunk_size; 394 } 395 396 } else { 397 // use the low level interface 398 399 // build a frame buffer (i.e. what we want on output) 400 Imf::FrameBuffer frameBuffer; 401 402 // allow dataWindow with minimal bounds different form zero 403 size_t offset = - dataWindow.min.x * bytespp - dataWindow.min.y * pitch; 404 405 if(components == 1) { 406 frameBuffer.insert ("Y", // name 407 Imf::Slice (pixelType, // type 408 (char*)(bits + offset), // base 409 bytespp, // xStride 410 pitch, // yStride 411 1, 1, // x/y sampling 412 0.0)); // fillValue 413 } else if((components == 3) || (components == 4)) { 414 const char *channel_name[4] = { "R", "G", "B", "A" }; 415 416 for(int c = 0; c < components; c++) { 417 frameBuffer.insert ( 418 channel_name[c], // name 419 Imf::Slice (pixelType, // type 420 (char*)(bits + c * sizeof(float) + offset), // base 421 bytespp, // xStride 422 pitch, // yStride 423 1, 1, // x/y sampling 424 0.0)); // fillValue 425 } 426 } 427 428 // read the file 429 file.setFrameBuffer(frameBuffer); 430 file.readPixels(dataWindow.min.y, dataWindow.max.y); 431 } 432 433 // lastly, flip dib lines 434 FreeImage_FlipVertical(dib); 435 436 } 437 catch(Iex::BaseExc & e) { 438 if(dib != NULL) { 439 FreeImage_Unload(dib); 440 } 441 FreeImage_OutputMessageProc(s_format_id, e.what()); 442 return NULL; 443 } 444 445 return dib; 446} 447 448/** 449Set the preview image using the dib embedded thumbnail 450*/ 451static BOOL 452SetPreviewImage(FIBITMAP *dib, Imf::Header& header) { 453 if(!FreeImage_GetThumbnail(dib)) { 454 return FALSE; 455 } 456 FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib); 457 458 if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 32)) { 459 // invalid thumbnail - ignore it 460 FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL); 461 } else { 462 const unsigned thWidth = FreeImage_GetWidth(thumbnail); 463 const unsigned thHeight = FreeImage_GetHeight(thumbnail); 464 465 Imf::PreviewImage preview(thWidth, thHeight); 466 467 // copy thumbnail to 32-bit RGBA preview image 468 469 const BYTE* src_line = FreeImage_GetScanLine(thumbnail, thHeight - 1); 470 Imf::PreviewRgba* dst_line = preview.pixels(); 471 const unsigned srcPitch = FreeImage_GetPitch(thumbnail); 472 473 for (unsigned y = 0; y < thHeight; y++) { 474 const RGBQUAD* src_pixel = (RGBQUAD*)src_line; 475 Imf::PreviewRgba* dst_pixel = dst_line; 476 477 for(unsigned x = 0; x < thWidth; x++) { 478 dst_pixel->r = src_pixel->rgbRed; 479 dst_pixel->g = src_pixel->rgbGreen; 480 dst_pixel->b = src_pixel->rgbBlue; 481 dst_pixel->a = src_pixel->rgbReserved; 482 483 src_pixel++; 484 dst_pixel++; 485 } 486 487 src_line -= srcPitch; 488 dst_line += thWidth; 489 } 490 491 header.setPreviewImage(preview); 492 } 493 494 return TRUE; 495} 496 497/** 498Save using EXR_LC compression (works only with RGB[A]F images) 499*/ 500static BOOL 501SaveAsEXR_LC(C_OStream& ostream, FIBITMAP *dib, Imf::Header& header, int width, int height) { 502 int x, y; 503 Imf::RgbaChannels rgbaChannels; 504 505 try { 506 507 FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); 508 509 // convert from float to half 510 Imf::Array2D<Imf::Rgba> pixels(height, width); 511 switch(image_type) { 512 case FIT_RGBF: 513 rgbaChannels = Imf::WRITE_YC; 514 for(y = 0; y < height; y++) { 515 FIRGBF *src_bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y); 516 for(x = 0; x < width; x++) { 517 Imf::Rgba &dst_bits = pixels[y][x]; 518 dst_bits.r = src_bits[x].red; 519 dst_bits.g = src_bits[x].green; 520 dst_bits.b = src_bits[x].blue; 521 } 522 } 523 break; 524 case FIT_RGBAF: 525 rgbaChannels = Imf::WRITE_YCA; 526 for(y = 0; y < height; y++) { 527 FIRGBAF *src_bits = (FIRGBAF*)FreeImage_GetScanLine(dib, height - 1 - y); 528 for(x = 0; x < width; x++) { 529 Imf::Rgba &dst_bits = pixels[y][x]; 530 dst_bits.r = src_bits[x].red; 531 dst_bits.g = src_bits[x].green; 532 dst_bits.b = src_bits[x].blue; 533 dst_bits.a = src_bits[x].alpha; 534 } 535 } 536 break; 537 default: 538 THROW (Iex::IoExc, "Bad image type"); 539 break; 540 } 541 542 // write the data 543 Imf::RgbaOutputFile file(ostream, header, rgbaChannels); 544 file.setFrameBuffer (&pixels[0][0], 1, width); 545 file.writePixels (height); 546 547 return TRUE; 548 549 } catch(Iex::BaseExc & e) { 550 FreeImage_OutputMessageProc(s_format_id, e.what()); 551 552 return FALSE; 553 } 554 555} 556 557static BOOL DLL_CALLCONV 558Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { 559 const char *channel_name[4] = { "R", "G", "B", "A" }; 560 BOOL bIsFlipped = FALSE; 561 half *halfData = NULL; 562 563 if(!dib || !handle) return FALSE; 564 565 try { 566 // check for EXR_LC compression and verify that the format is RGB 567 if((flags & EXR_LC) == EXR_LC) { 568 FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); 569 if(((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) { 570 THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images"); 571 } 572 if((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) { 573 THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2"); 574 } 575 } 576 577 // wrap the FreeImage IO stream 578 C_OStream ostream(io, handle); 579 580 // compression 581 Imf::Compression compress; 582 if((flags & EXR_NONE) == EXR_NONE) { 583 // no compression 584 compress = Imf::NO_COMPRESSION; 585 } else if((flags & EXR_ZIP) == EXR_ZIP) { 586 // zlib compression, in blocks of 16 scan lines 587 compress = Imf::ZIP_COMPRESSION; 588 } else if((flags & EXR_PIZ) == EXR_PIZ) { 589 // piz-based wavelet compression 590 compress = Imf::PIZ_COMPRESSION; 591 } else if((flags & EXR_PXR24) == EXR_PXR24) { 592 // lossy 24-bit float compression 593 compress = Imf::PXR24_COMPRESSION; 594 } else if((flags & EXR_B44) == EXR_B44) { 595 // lossy 44% float compression 596 compress = Imf::B44_COMPRESSION; 597 } else { 598 // default value 599 compress = Imf::PIZ_COMPRESSION; 600 } 601 602 // create the header 603 int width = FreeImage_GetWidth(dib); 604 int height = FreeImage_GetHeight(dib); 605 int dx = 0, dy = 0; 606 607 Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1)); 608 Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1)); 609 610 Imf::Header header = Imf::Header(displayWindow, dataWindow, 1, 611 Imath::V2f(0,0), 1, 612 Imf::INCREASING_Y, compress); 613 614 // handle thumbnail 615 SetPreviewImage(dib, header); 616 617 // check for EXR_LC compression 618 if((flags & EXR_LC) == EXR_LC) { 619 return SaveAsEXR_LC(ostream, dib, header, width, height); 620 } 621 622 // output pixel type 623 Imf::PixelType pixelType; 624 if((flags & EXR_FLOAT) == EXR_FLOAT) { 625 pixelType = Imf::FLOAT; // save as float data type 626 } else { 627 // default value 628 pixelType = Imf::HALF; // save as half data type 629 } 630 631 // check the data type and number of channels 632 int components = 0; 633 FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); 634 switch(image_type) { 635 case FIT_FLOAT: 636 components = 1; 637 // insert luminance channel 638 header.channels().insert ("Y", Imf::Channel(pixelType)); 639 break; 640 case FIT_RGBF: 641 components = 3; 642 for(int c = 0; c < components; c++) { 643 // insert R, G and B channels 644 header.channels().insert (channel_name[c], Imf::Channel(pixelType)); 645 } 646 break; 647 case FIT_RGBAF: 648 components = 4; 649 for(int c = 0; c < components; c++) { 650 // insert R, G, B and A channels 651 header.channels().insert (channel_name[c], Imf::Channel(pixelType)); 652 } 653 break; 654 default: 655 THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR."); 656 } 657 658 // build a frame buffer (i.e. what we have on input) 659 Imf::FrameBuffer frameBuffer; 660 661 BYTE *bits = NULL; // pointer to our pixel buffer 662 size_t bytespp = 0; // size of our pixel in bytes 663 size_t bytespc = 0; // size of our pixel component in bytes 664 unsigned pitch = 0; // size of our yStride in bytes 665 666 667 if(pixelType == Imf::HALF) { 668 // convert from float to half 669 halfData = new(std::nothrow) half[width * height * components]; 670 if(!halfData) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); 671 672 for(int y = 0; y < height; y++) { 673 float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); 674 half *dst_bits = halfData + y * width * components; 675 for(int x = 0; x < width; x++) { 676 for(int c = 0; c < components; c++) { 677 dst_bits[c] = src_bits[c]; 678 } 679 src_bits += components; 680 dst_bits += components; 681 } 682 } 683 bits = (BYTE*)halfData; 684 bytespc = sizeof(half); 685 bytespp = sizeof(half) * components; 686 pitch = sizeof(half) * width * components; 687 } else if(pixelType == Imf::FLOAT) { 688 // invert dib scanlines 689 bIsFlipped = FreeImage_FlipVertical(dib); 690 691 bits = FreeImage_GetBits(dib); 692 bytespc = sizeof(float); 693 bytespp = sizeof(float) * components; 694 pitch = FreeImage_GetPitch(dib); 695 } 696 697 if(image_type == FIT_FLOAT) { 698 frameBuffer.insert ("Y", // name 699 Imf::Slice (pixelType, // type 700 (char*)(bits), // base 701 bytespp, // xStride 702 pitch)); // yStride 703 } else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) { 704 for(int c = 0; c < components; c++) { 705 char *channel_base = (char*)(bits) + c*bytespc; 706 frameBuffer.insert (channel_name[c],// name 707 Imf::Slice (pixelType, // type 708 channel_base, // base 709 bytespp, // xStride 710 pitch)); // yStride 711 } 712 } 713 714 // write the data 715 Imf::OutputFile file (ostream, header); 716 file.setFrameBuffer (frameBuffer); 717 file.writePixels (height); 718 719 if(halfData != NULL) delete[] halfData; 720 if(bIsFlipped) { 721 // invert dib scanlines 722 FreeImage_FlipVertical(dib); 723 } 724 725 return TRUE; 726 727 } catch(Iex::BaseExc & e) { 728 if(halfData != NULL) delete[] halfData; 729 if(bIsFlipped) { 730 // invert dib scanlines 731 FreeImage_FlipVertical(dib); 732 } 733 734 FreeImage_OutputMessageProc(s_format_id, e.what()); 735 736 return FALSE; 737 } 738} 739 740// ========================================================== 741// Init 742// ========================================================== 743 744void DLL_CALLCONV 745InitEXR(Plugin *plugin, int format_id) { 746 s_format_id = format_id; 747 748 plugin->format_proc = Format; 749 plugin->description_proc = Description; 750 plugin->extension_proc = Extension; 751 plugin->regexpr_proc = RegExpr; 752 plugin->open_proc = NULL; 753 plugin->close_proc = NULL; 754 plugin->pagecount_proc = NULL; 755 plugin->pagecapability_proc = NULL; 756 plugin->load_proc = Load; 757 plugin->save_proc = Save; 758 plugin->validate_proc = Validate; 759 plugin->mime_proc = MimeType; 760 plugin->supports_export_bpp_proc = SupportsExportDepth; 761 plugin->supports_export_type_proc = SupportsExportType; 762 plugin->supports_icc_profiles_proc = NULL; 763 plugin->supports_no_pixels_proc = SupportsNoPixels; 764}