PageRenderTime 70ms CodeModel.GetById 15ms app.highlight 49ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/cabalistic/ogredeps/
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}