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