fixed: auto_ptr -> unique_ptr
[opensg.git] / Source / System / Image / FileIO / OSGEXRImageFileType.cpp
bloba1cd39fc62a10f8c96df5cd6df22aaa6fe1bd7c5
1 /*---------------------------------------------------------------------------*\
2 * OpenSG *
3 * *
4 * *
5 * Copyright (C) 2000-2002 by the OpenSG Forum *
6 * *
7 * www.opensg.org *
8 * *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
10 * *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
13 * License *
14 * *
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
18 * *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
27 * *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
30 * Changes *
31 * *
32 * *
33 * *
34 * *
35 * *
36 * *
37 \*---------------------------------------------------------------------------*/
39 //-------------------------------
40 // Includes
41 //-------------------------------
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <math.h>
46 #include <memory.h>
48 #include "OSGConfig.h"
50 #include <iostream>
51 #include <fstream>
52 #include <errno.h>
54 #ifdef _WIN32
55 #ifdef min
56 #undef min
57 #endif
58 #ifdef max
59 #undef max
60 #endif
61 #endif
63 #ifdef OSG_WITH_IMF
64 #include <OpenEXR/Iex.h>
65 #include <OpenEXR/ImathBox.h>
66 #include <OpenEXR/ImfIO.h>
67 #include <OpenEXR/ImfChannelList.h>
68 #include <OpenEXR/ImfHeader.h>
69 #include <OpenEXR/ImfVersion.h>
70 #include <OpenEXR/ImfArray.h>
71 #include <OpenEXR/ImfRgba.h>
72 #include <OpenEXR/ImfRgbaFile.h>
73 #include <OpenEXR/ImfInputFile.h>
74 #include <OpenEXR/ImfOutputFile.h>
75 #include <OpenEXR/ImfStandardAttributes.h>
76 #include <OpenEXR/ImfLineOrder.h>
77 #include <OpenEXR/ImfCompression.h>
78 #endif
80 #include "OSGLog.h"
81 #include "OSGImageFileHandler.h"
82 #include "OSGPathHandler.h"
83 #include "OSGFileSystem.h"
84 #include "OSGImageGenericAtt.h"
86 #include "OSGEXRImageFileType.h"
88 // Static Class Varible implementations:
89 static const OSG::Char8 *suffixArray[] =
91 "exr"
94 OSG_BEGIN_NAMESPACE
96 /*! \class EXRImageFileType
98 Image File Type to read/write and store/restore Image objects as EXR data.
99 Does depend on external libs, namely OpenEXR (by ILM).
101 Needs to be build with OSG_WITH_IMF
105 /*****************************
106 * Types
107 *****************************/
109 EXRImageFileType EXRImageFileType::_the("image/x-exr",
110 suffixArray, sizeof(suffixArray),
111 (OSG_READ_SUPPORTED |
112 OSG_WRITE_SUPPORTED) );
115 /*****************************
116 * Classvariables
117 *****************************/
120 /********************************
121 * Class methodes
122 *******************************/
124 #ifdef OSG_WITH_IMF
126 //-------------------------------------------------------------------------
127 /** class StdIFStream -- an implementation of
128 class IStream based on class std::istream
131 class StdIStream : public Imf::IStream
133 public:
134 /** A constructor that uses a std::istream that has already
135 been opened by the caller. The StdIStream's destructor
136 will not close the std::istream.
138 StdIStream(std::istream &is, const char fileName[]);
139 virtual ~StdIStream();
141 virtual bool read(char c[], int n);
142 virtual Imf::Int64 tellg();
143 virtual void seekg(Imf::Int64 pos);
144 virtual void clear();
146 private:
147 bool checkError();
149 std::istream *_is;
151 StdIStream(const StdIStream &other);
152 void operator =(const StdIStream &rhs);
155 //-------------------------------------------------------------------------
156 /** class StdOStream -- an implementation of
157 class OStream based on class std::ostream
159 class StdOStream : public Imf::OStream
161 public:
162 /** A constructor that uses a std::ostream that has already
163 been opened by the caller. The StdOStream's destructor
164 will not close the std::ostream.
166 StdOStream(std::ostream &os, const char fileName[]);
167 virtual ~StdOStream();
169 virtual void write(const char c[], int n);
170 virtual Imf::Int64 tellp();
171 virtual void seekp(Imf::Int64 pos);
173 private:
174 void checkError();
176 std::ostream *_os;
178 StdOStream(const StdOStream &other);
179 void operator =(const StdOStream &rhs);
182 //-------------------------------------------------------------------------
184 StdIStream::StdIStream(std::istream &is, const char aFileName[]) :
185 Imf::IStream(aFileName), _is(&is)
189 StdIStream::~StdIStream()
193 bool StdIStream::read(char c[], int n)
195 if (!*_is)
196 throw Iex::InputExc("Unexpected end of file.");
198 errno = 0;
199 _is->read(c, n);
201 return checkError();
204 Imf::Int64 StdIStream::tellg()
206 return std::streamoff(_is->tellg());
209 void StdIStream::seekg(Imf::Int64 pos)
211 _is->seekg(pos);
212 checkError();
215 void StdIStream::clear()
217 _is->clear();
220 bool StdIStream::checkError()
222 if (!*_is)
224 if (errno)
225 Iex::throwErrnoExc();
227 _is->clear(std::ios_base::goodbit);
228 return false;
230 return true;
233 //-------------------------------------------------------------------------
234 StdOStream::StdOStream(std::ostream &os, const char aFileName[]) :
235 Imf::OStream(aFileName), _os(&os) { }
237 StdOStream::~StdOStream() { }
239 void StdOStream::write(const char c[], int n)
241 errno = 0;
242 _os->write(c, n);
243 checkError();
246 Imf::Int64 StdOStream::tellp()
248 return std::streamoff(_os->tellp());
251 void StdOStream::seekp(Imf::Int64 pos)
253 _os->seekp(pos);
254 checkError();
257 void StdOStream::checkError()
259 if (!*_os)
261 if (errno)
262 Iex::throwErrnoExc();
263 throw Iex::ErrnoExc ("Write failed.");
267 #endif //OSG_WITH_IMF
270 /*******************************
271 *public
272 *******************************/
274 //-------------------------------------------------------------------------
276 Tries to fill the image object with the data read from
277 the given input stream. Returns true on success.
279 bool EXRImageFileType::read( Image *image,
280 std::istream &is,
281 const std::string &mimetype)
283 #ifdef OSG_WITH_IMF
284 if (!is.good())
285 return false;
287 const char *dummy = "";
288 StdIStream file(is, dummy);
290 Imf::Int64 pos = file.tellg();
292 bool check = isOpenExrFile(is);
294 file.seekg(pos);
296 if (!check)
298 FFATAL(( "Wrong format, no %s image given!\n",
299 mimetype.c_str() ));
300 return false;
303 // just read the header and get the channel count
304 int channel_count = 0;
305 pos = file.tellg();
308 Imf::RgbaInputFile stream(file);
309 const Imf::Header &header = stream.header();
310 const Imf::ChannelList &channels = header.channels();
312 for(Imf::ChannelList::ConstIterator it = channels.begin();
313 it != channels.end();
314 ++it)
316 ++channel_count;
319 catch(std::exception &e)
321 FFATAL(( "Error while trying to read OpenEXR Image from stream: %s\n",
322 e.what() ));
323 return false;
325 file.seekg(pos);
327 if(channel_count <= 4)
329 // TODO: check for mipmap levels,
330 // look if line order is s.th. else than INCREASING_Y
333 Int32 width, height, numImg = 1;
334 Imf::RgbaInputFile stream(file);
336 Imath::Box2i dw = stream.dataWindow();
337 Imf::Array2D<Imf::Rgba> pixels;
339 const Imf::Header &header = stream.header();
340 // const Imf::LineOrder &order = header.lineOrder();
342 const Imf::EnvmapAttribute *envmap =
343 header.findTypedAttribute<Imf::EnvmapAttribute>("envmap");
345 width = dw.max.x - dw.min.x + 1;
346 height = dw.max.y - dw.min.y + 1;
348 pixels.resizeErase(height, width);
350 if(envmap && envmap->value() == Imf::ENVMAP_CUBE)
352 numImg = 6;
353 height /= numImg;
355 if (width != height)
357 FFATAL(( "Cubemaps must have squared size, "
358 "but w=%d and h=%d!\n", width, height ));
359 return false;
363 stream.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y * width,
365 width);
366 stream.readPixels(dw.min.y, dw.max.y);
368 image->set( Image::OSG_RGBA_PF, width, height, 1, 1, 1, 0, 0,
369 Image::OSG_FLOAT16_IMAGEDATA, true, numImg );
370 image->clearHalf();
372 // now add custom attributes
373 for(Imf::Header::ConstIterator it = header.begin();
374 it != header.end();
375 ++it)
377 Imf::Attribute *copy = it.attribute().copy();
378 Imf::StringAttribute *sa =
379 dynamic_cast<Imf::StringAttribute *>(copy);
381 if(sa != NULL)
382 image->setAttachmentField(it.name(), sa->value());
384 delete copy;
387 Real16 *data = reinterpret_cast<Real16*>(image->editData());
389 for (Int32 side=numImg-1; side >=0; side--)
391 Int32 i, j, size = side * width * height * 4;
393 for (Int32 y=side*height; y<(side+1)*height; y++)
395 for (Int32 x=0; x<width; x++)
397 if (numImg == 1 || side == 2 || side == 3)
399 i = (2 * side + 1) * height - (y + 1); // new y
400 j = x;
402 else
404 i = y;
405 j = width - x - 1; // new x
408 *(data + size++) = Real16(pixels[i][j].r);
409 *(data + size++) = Real16(pixels[i][j].g);
410 *(data + size++) = Real16(pixels[i][j].b);
411 *(data + size++) = Real16(pixels[i][j].a);
416 return true;
418 catch(std::exception &e)
420 FFATAL(( "Error while trying to read OpenEXR Image from stream: "
421 "%s\n", e.what() ));
423 return false;
426 else
430 if(channel_count % 4 != 0)
432 FFATAL(( "Error while trying to read OpenEXR Image from "
433 "stream, channel count of %d is not supported!\n",
434 channel_count));
436 return false;
439 int num_img = channel_count / 4;
441 Imf::InputFile stream(file);
442 const Imf::Header &header = stream.header();
443 Imath::Box2i dw = header.dataWindow();
445 int width = dw.max.x - dw.min.x + 1;
446 int height = dw.max.y - dw.min.y + 1;
448 image->set(Image::OSG_RGBA_PF, width, height, 1, 1, 1, 0, 0,
449 Image::OSG_FLOAT16_IMAGEDATA, true, num_img);
450 image->clearHalf();
452 // now add custom attributes
453 for(Imf::Header::ConstIterator it = header.begin();
454 it != header.end();
455 ++it )
457 Imf::Attribute *copy = it.attribute().copy();
458 Imf::StringAttribute *sa =
459 dynamic_cast<Imf::StringAttribute *>(copy);
461 if(sa != NULL)
462 image->setAttachmentField(it.name(), sa->value());
464 delete copy;
467 const Imf::ChannelList &channels = header.channels();
469 // do some channel name checks
470 bool channel_error = false;
471 for(Imf::ChannelList::ConstIterator it=channels.begin();it!=channels.end();++it)
473 for(int side=0;side>num_img;++side)
475 char cn[20];
476 sprintf(cn, "%d", side);
478 char name[20];
479 sprintf(name, "R%s", side == 0 ? "" : cn);
480 if(channels.findChannel(name) == NULL)
481 channel_error = true;
482 sprintf(name, "G%s", side == 0 ? "" : cn);
483 if(channels.findChannel(name) == NULL)
484 channel_error = true;
485 sprintf(name, "B%s", side == 0 ? "" : cn);
486 if(channels.findChannel(name) == NULL)
487 channel_error = true;
488 sprintf(name, "A%s", side == 0 ? "" : cn);
489 if(channels.findChannel(name) == NULL)
490 channel_error = true;
494 if(channel_error)
496 FFATAL(( "Error while trying to read OpenEXR Image from "
497 "stream, expected channel names 'R' 'G' 'B' 'A', "
498 "'R1' 'G1', 'B1', 'A1', ...\n"));
500 return false;
503 Imf::FrameBuffer frame_buffer;
505 // we need to do a vertical flip so we read single scan lines in.
506 int current_scan_line = 0;
507 for(int i=height-1;i>=0;--i)
509 for(int side=0;side<num_img;++side)
511 char *data =
512 (reinterpret_cast<char *>(image->editData(0, 0, side))) + i * (sizeof(Real16) * 4 * width);
514 data -= current_scan_line * (sizeof(Real16) * 4 * width);
516 char cn[20];
517 sprintf(cn, "%d", side);
519 char name[20];
520 sprintf(name, "R%s", side == 0 ? "" : cn);
521 frame_buffer.insert(name, Imf::Slice(Imf::HALF, data, sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
522 sprintf(name, "G%s", side == 0 ? "" : cn);
523 frame_buffer.insert(name, Imf::Slice(Imf::HALF, data + 1 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
524 sprintf(name, "B%s", side == 0 ? "" : cn);
525 frame_buffer.insert(name, Imf::Slice(Imf::HALF, data + 2 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
526 sprintf(name, "A%s", side == 0 ? "" : cn);
527 frame_buffer.insert(name, Imf::Slice(Imf::HALF, data + 3 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
529 stream.setFrameBuffer(frame_buffer);
530 stream.readPixels(current_scan_line, current_scan_line);
531 ++current_scan_line;
534 return true;
536 catch(std::exception &e)
538 FFATAL(( "Error while trying to read OpenEXR Image from stream: %s\n",
539 e.what() ));
540 return false;
544 #else
545 SWARNING << getMimeType()
546 << " read is not compiled into the current binary "
547 << std::endl;
548 return false;
549 #endif
552 //-------------------------------------------------------------------------
554 Tries to write the image object to the given output stream.
555 Returns true on success.
557 bool EXRImageFileType::write(const Image *image,
558 std::ostream &os,
559 const std::string &mimetype)
561 #ifdef OSG_WITH_IMF
562 if (!os.good())
563 return false;
565 if(image->getDataType() != Image::OSG_FLOAT16_IMAGEDATA)
567 FWARNING(("EXRImageFileType::write: Image has non float data type!\n"));
568 return false;
570 if(image->getComponents() != 4)
572 FWARNING(("EXRImageFileType::write: Image has != 4 components!\n"));
573 return false;
575 if (image->getSideCount() == 6)
577 FWARNING(("EXRImageFileType::write: NYI for cubemaps\n")); //TODO
578 return false;
583 Int32 width = image->getWidth();
584 Int32 height = image->getHeight();
586 const char *dummy = "";
587 StdOStream file(os, dummy);
588 Imf::Header header(width, height);
590 #if 0
591 // now add custom attributes
592 ImageGenericAtt *att = dynamic_cast<ImageGenericAtt *>(
593 image->findAttachment(
594 ImageGenericAtt::getClassType().getGroupId()));
596 if(att != NULL)
598 FieldContainerType &fcType = att->getType();
599 Int32 count = att->getType().getNumFieldDescs();
601 for(Int32 i = 1; i <= count; ++i)
603 FieldDescriptionBase *fDesc = fcType.getFieldDesc(i);
604 Field *field = att->getField(i);
606 if(fDesc != NULL && field != NULL)
608 SFString *strField = dynamic_cast<SFString*>(field);
610 if(strField != NULL)
612 Imf::StringAttribute imfAttr(
613 strField->getValue().c_str());
615 header.insert(fDesc->getCName(), imfAttr);
620 #endif
622 // we write each side as 4 channels out
623 // side 0 RGBA
624 // side 1 R1G1B1A1
625 // ...
626 for(Int32 side=0;side<image->getSideCount();++side)
628 char cn[20];
629 sprintf(cn, "%d", side);
631 char name[20];
632 sprintf(name, "R%s", side == 0 ? "" : cn);
633 header.channels().insert(name, Imf::Channel(Imf::HALF));
634 sprintf(name, "G%s", side == 0 ? "" : cn);
635 header.channels().insert(name, Imf::Channel(Imf::HALF));
636 sprintf(name, "B%s", side == 0 ? "" : cn);
637 header.channels().insert(name, Imf::Channel(Imf::HALF));
638 sprintf(name, "A%s", side == 0 ? "" : cn);
639 header.channels().insert(name, Imf::Channel(Imf::HALF));
642 Imf::OutputFile stream(file, header);
643 Imf::FrameBuffer frame_buffer;
645 // we need to do a vertical flip so we write single scan lines out.
646 for(int i=height-1;i>=0;--i)
648 for(Int32 side=0;side<image->getSideCount();++side)
650 const char *data = (reinterpret_cast<const char *>(image->getData(0, 0, side))) + i * (sizeof(Real16) * 4 * width);
652 // writePixels() adds the current scan line index as an offset to the
653 // base address we need to eliminate this!
654 data -= stream.currentScanLine() * (sizeof(Real16) * 4 * width);
656 char cn[20];
657 sprintf(cn, "%d", side);
658 char name[20];
659 sprintf(name, "R%s", side == 0 ? "" : cn);
660 frame_buffer.insert(name, Imf::Slice(Imf::HALF,
661 const_cast<char *>(data), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
662 sprintf(name, "G%s", side == 0 ? "" : cn);
663 frame_buffer.insert(name, Imf::Slice(Imf::HALF,
664 const_cast<char *>(data) + 1 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
665 sprintf(name, "B%s", side == 0 ? "" : cn);
666 frame_buffer.insert(name, Imf::Slice(Imf::HALF,
667 const_cast<char *>(data) + 2 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
668 sprintf(name, "A%s", side == 0 ? "" : cn);
669 frame_buffer.insert(name, Imf::Slice(Imf::HALF,
670 const_cast<char *>(data) + 3 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
672 stream.setFrameBuffer(frame_buffer);
673 stream.writePixels(1);
676 return true;
678 catch(std::exception &e)
680 FFATAL(( "Error while trying to write OpenEXR Image from stream: %s\n",
681 e.what() ));
682 return false;
685 #else
686 SWARNING << getMimeType()
687 << " write is not compiled into the current binary "
688 << std::endl;
689 return false;
690 #endif
693 //-------------------------------------------------------------------------
695 Tries to restore the image data from the given memblock.
696 Returns the amount of data read.
698 UInt64 EXRImageFileType::restoreData( Image *image,
699 const UChar8 *buffer,
700 Int32 OSG_CHECK_ARG(memSize)) const
702 image->setData(buffer);
704 return image->getSize();
707 //-------------------------------------------------------------------------
709 Tries to store the image data to the given memblock.
710 Returns the amount of data written.
712 UInt64 EXRImageFileType::storeData(const Image *image,
713 UChar8 *buffer,
714 Int32 OSG_CHECK_ARG(memSize)) const
716 UInt32 dataSize = image->getSize();
718 const UChar8 *src = image->getData();
720 if(dataSize && src && buffer)
721 memcpy(buffer, src, dataSize);
723 return dataSize;
726 //-------------------------------------------------------------------------
728 Constructor used for the singleton object
730 EXRImageFileType::EXRImageFileType(const Char8 *mimeType,
731 const Char8 *suffixArray[],
732 UInt16 suffixByteCount,
733 UInt32 flags ) :
734 Inherited(mimeType,
735 suffixArray,
736 suffixByteCount,
737 flags )
739 FINFO(( "EXRImageFileType: %s\n", mimeType));
741 // TODO; needed for initialization of the library's global variables
742 //Imf::staticInitialize();
745 //-------------------------------------------------------------------------
747 Destructor
749 EXRImageFileType::~EXRImageFileType(void)
753 //-------------------------------------------------------------------------
755 Quick test for checking if file format is correct
757 bool EXRImageFileType::isOpenExrFile(std::istream &is)
759 #ifdef OSG_WITH_IMF
760 char bytes[4];
762 is.read(bytes, sizeof(bytes));
764 return !!is && Imf::isImfMagic(bytes);
766 #else
767 return false;
768 #endif
771 OSG_END_NAMESPACE