1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2000-2002 by the OpenSG Forum *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
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. *
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. *
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. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
39 //-------------------------------
41 //-------------------------------
48 #include "OSGConfig.h"
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>
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
[] =
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 /*****************************
107 *****************************/
109 EXRImageFileType
EXRImageFileType::_the("image/x-exr",
110 suffixArray
, sizeof(suffixArray
),
111 (OSG_READ_SUPPORTED
|
112 OSG_WRITE_SUPPORTED
) );
115 /*****************************
117 *****************************/
120 /********************************
122 *******************************/
126 //-------------------------------------------------------------------------
127 /** class StdIFStream -- an implementation of
128 class IStream based on class std::istream
131 class StdIStream
: public Imf::IStream
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();
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
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
);
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
)
196 throw Iex::InputExc("Unexpected end of file.");
204 Imf::Int64
StdIStream::tellg()
206 return std::streamoff(_is
->tellg());
209 void StdIStream::seekg(Imf::Int64 pos
)
215 void StdIStream::clear()
220 bool StdIStream::checkError()
225 Iex::throwErrnoExc();
227 _is
->clear(std::ios_base::goodbit
);
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
)
246 Imf::Int64
StdOStream::tellp()
248 return std::streamoff(_os
->tellp());
251 void StdOStream::seekp(Imf::Int64 pos
)
257 void StdOStream::checkError()
262 Iex::throwErrnoExc();
263 throw Iex::ErrnoExc ("Write failed.");
267 #endif //OSG_WITH_IMF
270 /*******************************
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
,
281 const std::string
&mimetype
)
287 const char *dummy
= "";
288 StdIStream
file(is
, dummy
);
290 Imf::Int64 pos
= file
.tellg();
292 bool check
= isOpenExrFile(is
);
298 FFATAL(( "Wrong format, no %s image given!\n",
303 // just read the header and get the channel count
304 int channel_count
= 0;
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();
319 catch(std::exception
&e
)
321 FFATAL(( "Error while trying to read OpenEXR Image from stream: %s\n",
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
)
357 FFATAL(( "Cubemaps must have squared size, "
358 "but w=%d and h=%d!\n", width
, height
));
363 stream
.setFrameBuffer(&pixels
[0][0] - dw
.min
.x
- dw
.min
.y
* 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
);
372 // now add custom attributes
373 for(Imf::Header::ConstIterator it
= header
.begin();
377 Imf::Attribute
*copy
= it
.attribute().copy();
378 Imf::StringAttribute
*sa
=
379 dynamic_cast<Imf::StringAttribute
*>(copy
);
382 image
->setAttachmentField(it
.name(), sa
->value());
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
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
);
418 catch(std::exception
&e
)
420 FFATAL(( "Error while trying to read OpenEXR Image from stream: "
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",
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
);
452 // now add custom attributes
453 for(Imf::Header::ConstIterator it
= header
.begin();
457 Imf::Attribute
*copy
= it
.attribute().copy();
458 Imf::StringAttribute
*sa
=
459 dynamic_cast<Imf::StringAttribute
*>(copy
);
462 image
->setAttachmentField(it
.name(), sa
->value());
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
)
476 sprintf(cn
, "%d", side
);
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;
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"));
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
)
512 (reinterpret_cast<char *>(image
->editData(0, 0, side
))) + i
* (sizeof(Real16
) * 4 * width
);
514 data
-= current_scan_line
* (sizeof(Real16
) * 4 * width
);
517 sprintf(cn
, "%d", side
);
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
);
536 catch(std::exception
&e
)
538 FFATAL(( "Error while trying to read OpenEXR Image from stream: %s\n",
545 SWARNING
<< getMimeType()
546 << " read is not compiled into the current binary "
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
,
559 const std::string
&mimetype
)
565 if(image
->getDataType() != Image::OSG_FLOAT16_IMAGEDATA
)
567 FWARNING(("EXRImageFileType::write: Image has non float data type!\n"));
570 if(image
->getComponents() != 4)
572 FWARNING(("EXRImageFileType::write: Image has != 4 components!\n"));
575 if (image
->getSideCount() == 6)
577 FWARNING(("EXRImageFileType::write: NYI for cubemaps\n")); //TODO
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
);
591 // now add custom attributes
592 ImageGenericAtt
*att
= dynamic_cast<ImageGenericAtt
*>(
593 image
->findAttachment(
594 ImageGenericAtt::getClassType().getGroupId()));
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
);
612 Imf::StringAttribute
imfAttr(
613 strField
->getValue().c_str());
615 header
.insert(fDesc
->getCName(), imfAttr
);
622 // we write each side as 4 channels out
626 for(Int32 side
=0;side
<image
->getSideCount();++side
)
629 sprintf(cn
, "%d", side
);
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
);
657 sprintf(cn
, "%d", side
);
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);
678 catch(std::exception
&e
)
680 FFATAL(( "Error while trying to write OpenEXR Image from stream: %s\n",
686 SWARNING
<< getMimeType()
687 << " write is not compiled into the current binary "
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
,
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
);
726 //-------------------------------------------------------------------------
728 Constructor used for the singleton object
730 EXRImageFileType::EXRImageFileType(const Char8
*mimeType
,
731 const Char8
*suffixArray
[],
732 UInt16 suffixByteCount
,
739 FINFO(( "EXRImageFileType: %s\n", mimeType
));
741 // TODO; needed for initialization of the library's global variables
742 //Imf::staticInitialize();
745 //-------------------------------------------------------------------------
749 EXRImageFileType::~EXRImageFileType(void)
753 //-------------------------------------------------------------------------
755 Quick test for checking if file format is correct
757 bool EXRImageFileType::isOpenExrFile(std::istream
&is
)
762 is
.read(bytes
, sizeof(bytes
));
764 return !!is
&& Imf::isImfMagic(bytes
);