/src/FreeImage/Source/FreeImage/PluginEXR.cpp

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