/[projet1]/public/pc/shared_libraries/freeimage/v3.12.0/Source/FreeImage/PluginEXR.cpp
Defence Force logotype

Contents of /public/pc/shared_libraries/freeimage/v3.12.0/Source/FreeImage/PluginEXR.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26